1f9bbe447SVladimir Oltean // SPDX-License-Identifier: GPL-2.0 2f9bbe447SVladimir Oltean /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com> 3f9bbe447SVladimir Oltean * 4f9bbe447SVladimir Oltean * This module is not a complete tagger implementation. It only provides 5f9bbe447SVladimir Oltean * primitives for taggers that rely on 802.1Q VLAN tags to use. The 6f9bbe447SVladimir Oltean * dsa_8021q_netdev_ops is registered for API compliance and not used 7f9bbe447SVladimir Oltean * directly by callers. 8f9bbe447SVladimir Oltean */ 9f9bbe447SVladimir Oltean #include <linux/if_bridge.h> 10f9bbe447SVladimir Oltean #include <linux/if_vlan.h> 11ac02a451SVladimir Oltean #include <linux/dsa/8021q.h> 12f9bbe447SVladimir Oltean 13f9bbe447SVladimir Oltean #include "dsa_priv.h" 14f9bbe447SVladimir Oltean 150471dd42SVladimir Oltean /* Binary structure of the fake 12-bit VID field (when the TPID is 160471dd42SVladimir Oltean * ETH_P_DSA_8021Q): 170471dd42SVladimir Oltean * 180471dd42SVladimir Oltean * | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 190471dd42SVladimir Oltean * +-----------+-----+-----------------+-----------+-----------------------+ 203eaae1d0SVladimir Oltean * | DIR | SVL | SWITCH_ID | SUBVLAN | PORT | 210471dd42SVladimir Oltean * +-----------+-----+-----------------+-----------+-----------------------+ 220471dd42SVladimir Oltean * 230471dd42SVladimir Oltean * DIR - VID[11:10]: 240471dd42SVladimir Oltean * Direction flags. 250471dd42SVladimir Oltean * * 1 (0b01) for RX VLAN, 260471dd42SVladimir Oltean * * 2 (0b10) for TX VLAN. 270471dd42SVladimir Oltean * These values make the special VIDs of 0, 1 and 4095 to be left 280471dd42SVladimir Oltean * unused by this coding scheme. 290471dd42SVladimir Oltean * 303eaae1d0SVladimir Oltean * SVL/SUBVLAN - { VID[9], VID[5:4] }: 313eaae1d0SVladimir Oltean * Sub-VLAN encoding. Valid only when DIR indicates an RX VLAN. 323eaae1d0SVladimir Oltean * * 0 (0b000): Field does not encode a sub-VLAN, either because 333eaae1d0SVladimir Oltean * received traffic is untagged, PVID-tagged or because a second 343eaae1d0SVladimir Oltean * VLAN tag is present after this tag and not inside of it. 353eaae1d0SVladimir Oltean * * 1 (0b001): Received traffic is tagged with a VID value private 363eaae1d0SVladimir Oltean * to the host. This field encodes the index in the host's lookup 373eaae1d0SVladimir Oltean * table through which the value of the ingress VLAN ID can be 383eaae1d0SVladimir Oltean * recovered. 393eaae1d0SVladimir Oltean * * 2 (0b010): Field encodes a sub-VLAN. 403eaae1d0SVladimir Oltean * ... 413eaae1d0SVladimir Oltean * * 7 (0b111): Field encodes a sub-VLAN. 423eaae1d0SVladimir Oltean * When DIR indicates a TX VLAN, SUBVLAN must be transmitted as zero 433eaae1d0SVladimir Oltean * (by the host) and ignored on receive (by the switch). 440471dd42SVladimir Oltean * 450471dd42SVladimir Oltean * SWITCH_ID - VID[8:6]: 46fcee85f1SVivien Didelot * Index of switch within DSA tree. Must be between 0 and 7. 470471dd42SVladimir Oltean * 480471dd42SVladimir Oltean * PORT - VID[3:0]: 49fcee85f1SVivien Didelot * Index of switch port. Must be between 0 and 15. 50f9bbe447SVladimir Oltean */ 510471dd42SVladimir Oltean 520471dd42SVladimir Oltean #define DSA_8021Q_DIR_SHIFT 10 530471dd42SVladimir Oltean #define DSA_8021Q_DIR_MASK GENMASK(11, 10) 540471dd42SVladimir Oltean #define DSA_8021Q_DIR(x) (((x) << DSA_8021Q_DIR_SHIFT) & \ 550471dd42SVladimir Oltean DSA_8021Q_DIR_MASK) 560471dd42SVladimir Oltean #define DSA_8021Q_DIR_RX DSA_8021Q_DIR(1) 570471dd42SVladimir Oltean #define DSA_8021Q_DIR_TX DSA_8021Q_DIR(2) 580471dd42SVladimir Oltean 590471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_SHIFT 6 600471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_MASK GENMASK(8, 6) 610471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID(x) (((x) << DSA_8021Q_SWITCH_ID_SHIFT) & \ 620471dd42SVladimir Oltean DSA_8021Q_SWITCH_ID_MASK) 630471dd42SVladimir Oltean 643eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN_HI_SHIFT 9 653eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN_HI_MASK GENMASK(9, 9) 663eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN_LO_SHIFT 4 673eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN_LO_MASK GENMASK(4, 3) 683eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN_HI(x) (((x) & GENMASK(2, 2)) >> 2) 693eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN_LO(x) ((x) & GENMASK(1, 0)) 703eaae1d0SVladimir Oltean #define DSA_8021Q_SUBVLAN(x) \ 713eaae1d0SVladimir Oltean (((DSA_8021Q_SUBVLAN_LO(x) << DSA_8021Q_SUBVLAN_LO_SHIFT) & \ 723eaae1d0SVladimir Oltean DSA_8021Q_SUBVLAN_LO_MASK) | \ 733eaae1d0SVladimir Oltean ((DSA_8021Q_SUBVLAN_HI(x) << DSA_8021Q_SUBVLAN_HI_SHIFT) & \ 743eaae1d0SVladimir Oltean DSA_8021Q_SUBVLAN_HI_MASK)) 753eaae1d0SVladimir Oltean 760471dd42SVladimir Oltean #define DSA_8021Q_PORT_SHIFT 0 770471dd42SVladimir Oltean #define DSA_8021Q_PORT_MASK GENMASK(3, 0) 780471dd42SVladimir Oltean #define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ 790471dd42SVladimir Oltean DSA_8021Q_PORT_MASK) 80f9bbe447SVladimir Oltean 81f9bbe447SVladimir Oltean /* Returns the VID to be inserted into the frame from xmit for switch steering 82f9bbe447SVladimir Oltean * instructions on egress. Encodes switch ID and port ID. 83f9bbe447SVladimir Oltean */ 84f9bbe447SVladimir Oltean u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) 85f9bbe447SVladimir Oltean { 860471dd42SVladimir Oltean return DSA_8021Q_DIR_TX | DSA_8021Q_SWITCH_ID(ds->index) | 870471dd42SVladimir Oltean DSA_8021Q_PORT(port); 88f9bbe447SVladimir Oltean } 89f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_tx_vid); 90f9bbe447SVladimir Oltean 91f9bbe447SVladimir Oltean /* Returns the VID that will be installed as pvid for this switch port, sent as 92f9bbe447SVladimir Oltean * tagged egress towards the CPU port and decoded by the rcv function. 93f9bbe447SVladimir Oltean */ 94f9bbe447SVladimir Oltean u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port) 95f9bbe447SVladimir Oltean { 960471dd42SVladimir Oltean return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(ds->index) | 970471dd42SVladimir Oltean DSA_8021Q_PORT(port); 98f9bbe447SVladimir Oltean } 99f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid); 100f9bbe447SVladimir Oltean 1013eaae1d0SVladimir Oltean u16 dsa_8021q_rx_vid_subvlan(struct dsa_switch *ds, int port, u16 subvlan) 1023eaae1d0SVladimir Oltean { 1033eaae1d0SVladimir Oltean return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(ds->index) | 1043eaae1d0SVladimir Oltean DSA_8021Q_PORT(port) | DSA_8021Q_SUBVLAN(subvlan); 1053eaae1d0SVladimir Oltean } 1063eaae1d0SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid_subvlan); 1073eaae1d0SVladimir Oltean 108f9bbe447SVladimir Oltean /* Returns the decoded switch ID from the RX VID. */ 109f9bbe447SVladimir Oltean int dsa_8021q_rx_switch_id(u16 vid) 110f9bbe447SVladimir Oltean { 1110471dd42SVladimir Oltean return (vid & DSA_8021Q_SWITCH_ID_MASK) >> DSA_8021Q_SWITCH_ID_SHIFT; 112f9bbe447SVladimir Oltean } 113f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_switch_id); 114f9bbe447SVladimir Oltean 115f9bbe447SVladimir Oltean /* Returns the decoded port ID from the RX VID. */ 116f9bbe447SVladimir Oltean int dsa_8021q_rx_source_port(u16 vid) 117f9bbe447SVladimir Oltean { 1180471dd42SVladimir Oltean return (vid & DSA_8021Q_PORT_MASK) >> DSA_8021Q_PORT_SHIFT; 119f9bbe447SVladimir Oltean } 120f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port); 121f9bbe447SVladimir Oltean 1223eaae1d0SVladimir Oltean /* Returns the decoded subvlan from the RX VID. */ 1233eaae1d0SVladimir Oltean u16 dsa_8021q_rx_subvlan(u16 vid) 1243eaae1d0SVladimir Oltean { 1253eaae1d0SVladimir Oltean u16 svl_hi, svl_lo; 1263eaae1d0SVladimir Oltean 1273eaae1d0SVladimir Oltean svl_hi = (vid & DSA_8021Q_SUBVLAN_HI_MASK) >> 1283eaae1d0SVladimir Oltean DSA_8021Q_SUBVLAN_HI_SHIFT; 1293eaae1d0SVladimir Oltean svl_lo = (vid & DSA_8021Q_SUBVLAN_LO_MASK) >> 1303eaae1d0SVladimir Oltean DSA_8021Q_SUBVLAN_LO_SHIFT; 1313eaae1d0SVladimir Oltean 1323eaae1d0SVladimir Oltean return (svl_hi << 2) | svl_lo; 1333eaae1d0SVladimir Oltean } 1343eaae1d0SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_subvlan); 1353eaae1d0SVladimir Oltean 1361f66b0f0SVladimir Oltean bool vid_is_dsa_8021q(u16 vid) 1371f66b0f0SVladimir Oltean { 1381f66b0f0SVladimir Oltean return ((vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_RX || 1391f66b0f0SVladimir Oltean (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_TX); 1401f66b0f0SVladimir Oltean } 1411f66b0f0SVladimir Oltean EXPORT_SYMBOL_GPL(vid_is_dsa_8021q); 1421f66b0f0SVladimir Oltean 1435f33183bSVladimir Oltean /* If @enabled is true, installs @vid with @flags into the switch port's HW 1445f33183bSVladimir Oltean * filter. 1455f33183bSVladimir Oltean * If @enabled is false, deletes @vid (ignores @flags) from the port. Had the 1465f33183bSVladimir Oltean * user explicitly configured this @vid through the bridge core, then the @vid 1475f33183bSVladimir Oltean * is installed again, but this time with the flags from the bridge layer. 1485f33183bSVladimir Oltean */ 1495899ee36SVladimir Oltean static int dsa_8021q_vid_apply(struct dsa_8021q_context *ctx, int port, u16 vid, 1505f33183bSVladimir Oltean u16 flags, bool enabled) 1515f33183bSVladimir Oltean { 1525899ee36SVladimir Oltean struct dsa_port *dp = dsa_to_port(ctx->ds, port); 1535f33183bSVladimir Oltean 1545f33183bSVladimir Oltean if (enabled) 1555899ee36SVladimir Oltean return ctx->ops->vlan_add(ctx->ds, dp->index, vid, flags); 1565f33183bSVladimir Oltean 1575899ee36SVladimir Oltean return ctx->ops->vlan_del(ctx->ds, dp->index, vid); 1585f33183bSVladimir Oltean } 1595f33183bSVladimir Oltean 160f9bbe447SVladimir Oltean /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single 161f9bbe447SVladimir Oltean * front-panel switch port (here swp0). 162f9bbe447SVladimir Oltean * 163f9bbe447SVladimir Oltean * Port identification through VLAN (802.1Q) tags has different requirements 164f9bbe447SVladimir Oltean * for it to work effectively: 165f9bbe447SVladimir Oltean * - On RX (ingress from network): each front-panel port must have a pvid 166f9bbe447SVladimir Oltean * that uniquely identifies it, and the egress of this pvid must be tagged 167f9bbe447SVladimir Oltean * towards the CPU port, so that software can recover the source port based 168f9bbe447SVladimir Oltean * on the VID in the frame. But this would only work for standalone ports; 169f9bbe447SVladimir Oltean * if bridged, this VLAN setup would break autonomous forwarding and would 170f9bbe447SVladimir Oltean * force all switched traffic to pass through the CPU. So we must also make 171f9bbe447SVladimir Oltean * the other front-panel ports members of this VID we're adding, albeit 172f9bbe447SVladimir Oltean * we're not making it their PVID (they'll still have their own). 173f9bbe447SVladimir Oltean * By the way - just because we're installing the same VID in multiple 174f9bbe447SVladimir Oltean * switch ports doesn't mean that they'll start to talk to one another, even 175f9bbe447SVladimir Oltean * while not bridged: the final forwarding decision is still an AND between 176f9bbe447SVladimir Oltean * the L2 forwarding information (which is limiting forwarding in this case) 177f9bbe447SVladimir Oltean * and the VLAN-based restrictions (of which there are none in this case, 178f9bbe447SVladimir Oltean * since all ports are members). 179f9bbe447SVladimir Oltean * - On TX (ingress from CPU and towards network) we are faced with a problem. 180f9bbe447SVladimir Oltean * If we were to tag traffic (from within DSA) with the port's pvid, all 181f9bbe447SVladimir Oltean * would be well, assuming the switch ports were standalone. Frames would 182f9bbe447SVladimir Oltean * have no choice but to be directed towards the correct front-panel port. 183f9bbe447SVladimir Oltean * But because we also want the RX VLAN to not break bridging, then 184f9bbe447SVladimir Oltean * inevitably that means that we have to give them a choice (of what 185f9bbe447SVladimir Oltean * front-panel port to go out on), and therefore we cannot steer traffic 186f9bbe447SVladimir Oltean * based on the RX VID. So what we do is simply install one more VID on the 187f9bbe447SVladimir Oltean * front-panel and CPU ports, and profit off of the fact that steering will 188f9bbe447SVladimir Oltean * work just by virtue of the fact that there is only one other port that's 189f9bbe447SVladimir Oltean * a member of the VID we're tagging the traffic with - the desired one. 190f9bbe447SVladimir Oltean * 191f9bbe447SVladimir Oltean * So at the end, each front-panel port will have one RX VID (also the PVID), 192f9bbe447SVladimir Oltean * the RX VID of all other front-panel ports, and one TX VID. Whereas the CPU 193f9bbe447SVladimir Oltean * port will have the RX and TX VIDs of all front-panel ports, and on top of 194f9bbe447SVladimir Oltean * that, is also tagged-input and tagged-output (VLAN trunk). 195f9bbe447SVladimir Oltean * 196f9bbe447SVladimir Oltean * CPU port CPU port 197f9bbe447SVladimir Oltean * +-------------+-----+-------------+ +-------------+-----+-------------+ 198f9bbe447SVladimir Oltean * | RX VID | | | | TX VID | | | 199f9bbe447SVladimir Oltean * | of swp0 | | | | of swp0 | | | 200f9bbe447SVladimir Oltean * | +-----+ | | +-----+ | 201f9bbe447SVladimir Oltean * | ^ T | | | Tagged | 202f9bbe447SVladimir Oltean * | | | | | ingress | 203f9bbe447SVladimir Oltean * | +-------+---+---+-------+ | | +-----------+ | 204f9bbe447SVladimir Oltean * | | | | | | | | Untagged | 205f9bbe447SVladimir Oltean * | | U v U v U v | | v egress | 206f9bbe447SVladimir Oltean * | +-----+ +-----+ +-----+ +-----+ | | +-----+ +-----+ +-----+ +-----+ | 207f9bbe447SVladimir Oltean * | | | | | | | | | | | | | | | | | | | | 208f9bbe447SVladimir Oltean * | |PVID | | | | | | | | | | | | | | | | | | 209f9bbe447SVladimir Oltean * +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+ 210f9bbe447SVladimir Oltean * swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3 211f9bbe447SVladimir Oltean */ 2125899ee36SVladimir Oltean static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port, 2135899ee36SVladimir Oltean bool enabled) 214f9bbe447SVladimir Oltean { 2155899ee36SVladimir Oltean int upstream = dsa_upstream_port(ctx->ds, port); 2165899ee36SVladimir Oltean u16 rx_vid = dsa_8021q_rx_vid(ctx->ds, port); 2175899ee36SVladimir Oltean u16 tx_vid = dsa_8021q_tx_vid(ctx->ds, port); 218f9bbe447SVladimir Oltean int i, err; 219f9bbe447SVladimir Oltean 220f9bbe447SVladimir Oltean /* The CPU port is implicitly configured by 221f9bbe447SVladimir Oltean * configuring the front-panel ports 222f9bbe447SVladimir Oltean */ 2235899ee36SVladimir Oltean if (!dsa_is_user_port(ctx->ds, port)) 224f9bbe447SVladimir Oltean return 0; 225f9bbe447SVladimir Oltean 226f9bbe447SVladimir Oltean /* Add this user port's RX VID to the membership list of all others 227f9bbe447SVladimir Oltean * (including itself). This is so that bridging will not be hindered. 228f9bbe447SVladimir Oltean * L2 forwarding rules still take precedence when there are no VLAN 229f9bbe447SVladimir Oltean * restrictions, so there are no concerns about leaking traffic. 230f9bbe447SVladimir Oltean */ 2315899ee36SVladimir Oltean for (i = 0; i < ctx->ds->num_ports; i++) { 232f9bbe447SVladimir Oltean u16 flags; 233f9bbe447SVladimir Oltean 234f9bbe447SVladimir Oltean if (i == upstream) 235d34d2baaSIoana Ciornei continue; 236f9bbe447SVladimir Oltean else if (i == port) 237f9bbe447SVladimir Oltean /* The RX VID is pvid on this port */ 238f9bbe447SVladimir Oltean flags = BRIDGE_VLAN_INFO_UNTAGGED | 239f9bbe447SVladimir Oltean BRIDGE_VLAN_INFO_PVID; 240f9bbe447SVladimir Oltean else 241f9bbe447SVladimir Oltean /* The RX VID is a regular VLAN on all others */ 242f9bbe447SVladimir Oltean flags = BRIDGE_VLAN_INFO_UNTAGGED; 243f9bbe447SVladimir Oltean 2445899ee36SVladimir Oltean err = dsa_8021q_vid_apply(ctx, i, rx_vid, flags, enabled); 245f9bbe447SVladimir Oltean if (err) { 2465899ee36SVladimir Oltean dev_err(ctx->ds->dev, 2475899ee36SVladimir Oltean "Failed to apply RX VID %d to port %d: %d\n", 248f9bbe447SVladimir Oltean rx_vid, port, err); 249f9bbe447SVladimir Oltean return err; 250f9bbe447SVladimir Oltean } 251f9bbe447SVladimir Oltean } 252d34d2baaSIoana Ciornei 253d34d2baaSIoana Ciornei /* CPU port needs to see this port's RX VID 254d34d2baaSIoana Ciornei * as tagged egress. 255d34d2baaSIoana Ciornei */ 2565899ee36SVladimir Oltean err = dsa_8021q_vid_apply(ctx, upstream, rx_vid, 0, enabled); 257d34d2baaSIoana Ciornei if (err) { 2585899ee36SVladimir Oltean dev_err(ctx->ds->dev, 2595899ee36SVladimir Oltean "Failed to apply RX VID %d to port %d: %d\n", 260d34d2baaSIoana Ciornei rx_vid, port, err); 261d34d2baaSIoana Ciornei return err; 262d34d2baaSIoana Ciornei } 263d34d2baaSIoana Ciornei 264f9bbe447SVladimir Oltean /* Finally apply the TX VID on this port and on the CPU port */ 2655899ee36SVladimir Oltean err = dsa_8021q_vid_apply(ctx, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED, 2665f33183bSVladimir Oltean enabled); 267f9bbe447SVladimir Oltean if (err) { 2685899ee36SVladimir Oltean dev_err(ctx->ds->dev, 2695899ee36SVladimir Oltean "Failed to apply TX VID %d on port %d: %d\n", 270f9bbe447SVladimir Oltean tx_vid, port, err); 271f9bbe447SVladimir Oltean return err; 272f9bbe447SVladimir Oltean } 2735899ee36SVladimir Oltean err = dsa_8021q_vid_apply(ctx, upstream, tx_vid, 0, enabled); 274f9bbe447SVladimir Oltean if (err) { 2755899ee36SVladimir Oltean dev_err(ctx->ds->dev, 2765899ee36SVladimir Oltean "Failed to apply TX VID %d on port %d: %d\n", 277f9bbe447SVladimir Oltean tx_vid, upstream, err); 278f9bbe447SVladimir Oltean return err; 279f9bbe447SVladimir Oltean } 280f9bbe447SVladimir Oltean 2815f33183bSVladimir Oltean return err; 282f9bbe447SVladimir Oltean } 2837e092af2SVladimir Oltean 2845899ee36SVladimir Oltean int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled) 2857e092af2SVladimir Oltean { 2867e092af2SVladimir Oltean int rc, port; 2877e092af2SVladimir Oltean 2885899ee36SVladimir Oltean for (port = 0; port < ctx->ds->num_ports; port++) { 2895899ee36SVladimir Oltean rc = dsa_8021q_setup_port(ctx, port, enabled); 2907e092af2SVladimir Oltean if (rc < 0) { 2915899ee36SVladimir Oltean dev_err(ctx->ds->dev, 2927e092af2SVladimir Oltean "Failed to setup VLAN tagging for port %d: %d\n", 2937e092af2SVladimir Oltean port, rc); 2947e092af2SVladimir Oltean return rc; 2957e092af2SVladimir Oltean } 2967e092af2SVladimir Oltean } 2977e092af2SVladimir Oltean 2987e092af2SVladimir Oltean return 0; 2997e092af2SVladimir Oltean } 3007e092af2SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_setup); 301f9bbe447SVladimir Oltean 3025899ee36SVladimir Oltean static int dsa_8021q_crosschip_link_apply(struct dsa_8021q_context *ctx, 3035899ee36SVladimir Oltean int port, 3045899ee36SVladimir Oltean struct dsa_8021q_context *other_ctx, 305ac02a451SVladimir Oltean int other_port, bool enabled) 306ac02a451SVladimir Oltean { 3075899ee36SVladimir Oltean u16 rx_vid = dsa_8021q_rx_vid(ctx->ds, port); 308ac02a451SVladimir Oltean 309ac02a451SVladimir Oltean /* @rx_vid of local @ds port @port goes to @other_port of 310ac02a451SVladimir Oltean * @other_ds 311ac02a451SVladimir Oltean */ 3125899ee36SVladimir Oltean return dsa_8021q_vid_apply(other_ctx, other_port, rx_vid, 313ac02a451SVladimir Oltean BRIDGE_VLAN_INFO_UNTAGGED, enabled); 314ac02a451SVladimir Oltean } 315ac02a451SVladimir Oltean 3165899ee36SVladimir Oltean static int dsa_8021q_crosschip_link_add(struct dsa_8021q_context *ctx, int port, 3175899ee36SVladimir Oltean struct dsa_8021q_context *other_ctx, 3185899ee36SVladimir Oltean int other_port) 319ac02a451SVladimir Oltean { 320ac02a451SVladimir Oltean struct dsa_8021q_crosschip_link *c; 321ac02a451SVladimir Oltean 3225899ee36SVladimir Oltean list_for_each_entry(c, &ctx->crosschip_links, list) { 3235899ee36SVladimir Oltean if (c->port == port && c->other_ctx == other_ctx && 324ac02a451SVladimir Oltean c->other_port == other_port) { 325ac02a451SVladimir Oltean refcount_inc(&c->refcount); 326ac02a451SVladimir Oltean return 0; 327ac02a451SVladimir Oltean } 328ac02a451SVladimir Oltean } 329ac02a451SVladimir Oltean 3305899ee36SVladimir Oltean dev_dbg(ctx->ds->dev, 3315899ee36SVladimir Oltean "adding crosschip link from port %d to %s port %d\n", 3325899ee36SVladimir Oltean port, dev_name(other_ctx->ds->dev), other_port); 333ac02a451SVladimir Oltean 334ac02a451SVladimir Oltean c = kzalloc(sizeof(*c), GFP_KERNEL); 335ac02a451SVladimir Oltean if (!c) 336ac02a451SVladimir Oltean return -ENOMEM; 337ac02a451SVladimir Oltean 338ac02a451SVladimir Oltean c->port = port; 3395899ee36SVladimir Oltean c->other_ctx = other_ctx; 340ac02a451SVladimir Oltean c->other_port = other_port; 341ac02a451SVladimir Oltean refcount_set(&c->refcount, 1); 342ac02a451SVladimir Oltean 3435899ee36SVladimir Oltean list_add(&c->list, &ctx->crosschip_links); 344ac02a451SVladimir Oltean 345ac02a451SVladimir Oltean return 0; 346ac02a451SVladimir Oltean } 347ac02a451SVladimir Oltean 3485899ee36SVladimir Oltean static void dsa_8021q_crosschip_link_del(struct dsa_8021q_context *ctx, 349ac02a451SVladimir Oltean struct dsa_8021q_crosschip_link *c, 350ac02a451SVladimir Oltean bool *keep) 351ac02a451SVladimir Oltean { 352ac02a451SVladimir Oltean *keep = !refcount_dec_and_test(&c->refcount); 353ac02a451SVladimir Oltean 354ac02a451SVladimir Oltean if (*keep) 355ac02a451SVladimir Oltean return; 356ac02a451SVladimir Oltean 3575899ee36SVladimir Oltean dev_dbg(ctx->ds->dev, 358ac02a451SVladimir Oltean "deleting crosschip link from port %d to %s port %d\n", 3595899ee36SVladimir Oltean c->port, dev_name(c->other_ctx->ds->dev), c->other_port); 360ac02a451SVladimir Oltean 361ac02a451SVladimir Oltean list_del(&c->list); 362ac02a451SVladimir Oltean kfree(c); 363ac02a451SVladimir Oltean } 364ac02a451SVladimir Oltean 365ac02a451SVladimir Oltean /* Make traffic from local port @port be received by remote port @other_port. 366ac02a451SVladimir Oltean * This means that our @rx_vid needs to be installed on @other_ds's upstream 367ac02a451SVladimir Oltean * and user ports. The user ports should be egress-untagged so that they can 368ac02a451SVladimir Oltean * pop the dsa_8021q VLAN. But the @other_upstream can be either egress-tagged 369ac02a451SVladimir Oltean * or untagged: it doesn't matter, since it should never egress a frame having 370ac02a451SVladimir Oltean * our @rx_vid. 371ac02a451SVladimir Oltean */ 3725899ee36SVladimir Oltean int dsa_8021q_crosschip_bridge_join(struct dsa_8021q_context *ctx, int port, 3735899ee36SVladimir Oltean struct dsa_8021q_context *other_ctx, 3745899ee36SVladimir Oltean int other_port) 375ac02a451SVladimir Oltean { 376ac02a451SVladimir Oltean /* @other_upstream is how @other_ds reaches us. If we are part 377ac02a451SVladimir Oltean * of disjoint trees, then we are probably connected through 378ac02a451SVladimir Oltean * our CPU ports. If we're part of the same tree though, we should 379ac02a451SVladimir Oltean * probably use dsa_towards_port. 380ac02a451SVladimir Oltean */ 3815899ee36SVladimir Oltean int other_upstream = dsa_upstream_port(other_ctx->ds, other_port); 382ac02a451SVladimir Oltean int rc; 383ac02a451SVladimir Oltean 3845899ee36SVladimir Oltean rc = dsa_8021q_crosschip_link_add(ctx, port, other_ctx, other_port); 385ac02a451SVladimir Oltean if (rc) 386ac02a451SVladimir Oltean return rc; 387ac02a451SVladimir Oltean 3885899ee36SVladimir Oltean rc = dsa_8021q_crosschip_link_apply(ctx, port, other_ctx, 389ac02a451SVladimir Oltean other_port, true); 390ac02a451SVladimir Oltean if (rc) 391ac02a451SVladimir Oltean return rc; 392ac02a451SVladimir Oltean 3935899ee36SVladimir Oltean rc = dsa_8021q_crosschip_link_add(ctx, port, other_ctx, other_upstream); 394ac02a451SVladimir Oltean if (rc) 395ac02a451SVladimir Oltean return rc; 396ac02a451SVladimir Oltean 3975899ee36SVladimir Oltean return dsa_8021q_crosschip_link_apply(ctx, port, other_ctx, 398ac02a451SVladimir Oltean other_upstream, true); 399ac02a451SVladimir Oltean } 400ac02a451SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_crosschip_bridge_join); 401ac02a451SVladimir Oltean 4025899ee36SVladimir Oltean int dsa_8021q_crosschip_bridge_leave(struct dsa_8021q_context *ctx, int port, 4035899ee36SVladimir Oltean struct dsa_8021q_context *other_ctx, 4045899ee36SVladimir Oltean int other_port) 405ac02a451SVladimir Oltean { 4065899ee36SVladimir Oltean int other_upstream = dsa_upstream_port(other_ctx->ds, other_port); 407ac02a451SVladimir Oltean struct dsa_8021q_crosschip_link *c, *n; 408ac02a451SVladimir Oltean 4095899ee36SVladimir Oltean list_for_each_entry_safe(c, n, &ctx->crosschip_links, list) { 4105899ee36SVladimir Oltean if (c->port == port && c->other_ctx == other_ctx && 411ac02a451SVladimir Oltean (c->other_port == other_port || 412ac02a451SVladimir Oltean c->other_port == other_upstream)) { 4135899ee36SVladimir Oltean struct dsa_8021q_context *other_ctx = c->other_ctx; 414ac02a451SVladimir Oltean int other_port = c->other_port; 415ac02a451SVladimir Oltean bool keep; 416ac02a451SVladimir Oltean int rc; 417ac02a451SVladimir Oltean 4185899ee36SVladimir Oltean dsa_8021q_crosschip_link_del(ctx, c, &keep); 419ac02a451SVladimir Oltean if (keep) 420ac02a451SVladimir Oltean continue; 421ac02a451SVladimir Oltean 4225899ee36SVladimir Oltean rc = dsa_8021q_crosschip_link_apply(ctx, port, 4235899ee36SVladimir Oltean other_ctx, 424ac02a451SVladimir Oltean other_port, 425ac02a451SVladimir Oltean false); 426ac02a451SVladimir Oltean if (rc) 427ac02a451SVladimir Oltean return rc; 428ac02a451SVladimir Oltean } 429ac02a451SVladimir Oltean } 430ac02a451SVladimir Oltean 431ac02a451SVladimir Oltean return 0; 432ac02a451SVladimir Oltean } 433ac02a451SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_crosschip_bridge_leave); 434ac02a451SVladimir Oltean 435f9bbe447SVladimir Oltean struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, 436f9bbe447SVladimir Oltean u16 tpid, u16 tci) 437f9bbe447SVladimir Oltean { 438f9bbe447SVladimir Oltean /* skb->data points at skb_mac_header, which 439f9bbe447SVladimir Oltean * is fine for vlan_insert_tag. 440f9bbe447SVladimir Oltean */ 441f9bbe447SVladimir Oltean return vlan_insert_tag(skb, htons(tpid), tci); 442f9bbe447SVladimir Oltean } 443f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_xmit); 444f9bbe447SVladimir Oltean 445f9bbe447SVladimir Oltean MODULE_LICENSE("GPL v2"); 446