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 * +-----------+-----+-----------------+-----------+-----------------------+ 200fac6aa0SVladimir Oltean * | DIR | RSV | SWITCH_ID | RSV | 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 * 300471dd42SVladimir Oltean * SWITCH_ID - VID[8:6]: 31fcee85f1SVivien Didelot * Index of switch within DSA tree. Must be between 0 and 7. 320471dd42SVladimir Oltean * 330fac6aa0SVladimir Oltean * RSV - VID[5:4]: 340fac6aa0SVladimir Oltean * To be used for further expansion of PORT or for other purposes. 350fac6aa0SVladimir Oltean * Must be transmitted as zero and ignored on receive. 360fac6aa0SVladimir Oltean * 370471dd42SVladimir Oltean * PORT - VID[3:0]: 38fcee85f1SVivien Didelot * Index of switch port. Must be between 0 and 15. 39f9bbe447SVladimir Oltean */ 400471dd42SVladimir Oltean 410471dd42SVladimir Oltean #define DSA_8021Q_DIR_SHIFT 10 420471dd42SVladimir Oltean #define DSA_8021Q_DIR_MASK GENMASK(11, 10) 430471dd42SVladimir Oltean #define DSA_8021Q_DIR(x) (((x) << DSA_8021Q_DIR_SHIFT) & \ 440471dd42SVladimir Oltean DSA_8021Q_DIR_MASK) 450471dd42SVladimir Oltean #define DSA_8021Q_DIR_RX DSA_8021Q_DIR(1) 460471dd42SVladimir Oltean #define DSA_8021Q_DIR_TX DSA_8021Q_DIR(2) 470471dd42SVladimir Oltean 480471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_SHIFT 6 490471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_MASK GENMASK(8, 6) 500471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID(x) (((x) << DSA_8021Q_SWITCH_ID_SHIFT) & \ 510471dd42SVladimir Oltean DSA_8021Q_SWITCH_ID_MASK) 520471dd42SVladimir Oltean 530471dd42SVladimir Oltean #define DSA_8021Q_PORT_SHIFT 0 540471dd42SVladimir Oltean #define DSA_8021Q_PORT_MASK GENMASK(3, 0) 550471dd42SVladimir Oltean #define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ 560471dd42SVladimir Oltean DSA_8021Q_PORT_MASK) 57f9bbe447SVladimir Oltean 58f9bbe447SVladimir Oltean /* Returns the VID to be inserted into the frame from xmit for switch steering 59f9bbe447SVladimir Oltean * instructions on egress. Encodes switch ID and port ID. 60f9bbe447SVladimir Oltean */ 61f9bbe447SVladimir Oltean u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) 62f9bbe447SVladimir Oltean { 630471dd42SVladimir Oltean return DSA_8021Q_DIR_TX | DSA_8021Q_SWITCH_ID(ds->index) | 640471dd42SVladimir Oltean DSA_8021Q_PORT(port); 65f9bbe447SVladimir Oltean } 66f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_tx_vid); 67f9bbe447SVladimir Oltean 68f9bbe447SVladimir Oltean /* Returns the VID that will be installed as pvid for this switch port, sent as 69f9bbe447SVladimir Oltean * tagged egress towards the CPU port and decoded by the rcv function. 70f9bbe447SVladimir Oltean */ 71f9bbe447SVladimir Oltean u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port) 72f9bbe447SVladimir Oltean { 730471dd42SVladimir Oltean return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(ds->index) | 740471dd42SVladimir Oltean DSA_8021Q_PORT(port); 75f9bbe447SVladimir Oltean } 76f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid); 77f9bbe447SVladimir Oltean 78f9bbe447SVladimir Oltean /* Returns the decoded switch ID from the RX VID. */ 79f9bbe447SVladimir Oltean int dsa_8021q_rx_switch_id(u16 vid) 80f9bbe447SVladimir Oltean { 810471dd42SVladimir Oltean return (vid & DSA_8021Q_SWITCH_ID_MASK) >> DSA_8021Q_SWITCH_ID_SHIFT; 82f9bbe447SVladimir Oltean } 83f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_switch_id); 84f9bbe447SVladimir Oltean 85f9bbe447SVladimir Oltean /* Returns the decoded port ID from the RX VID. */ 86f9bbe447SVladimir Oltean int dsa_8021q_rx_source_port(u16 vid) 87f9bbe447SVladimir Oltean { 880471dd42SVladimir Oltean return (vid & DSA_8021Q_PORT_MASK) >> DSA_8021Q_PORT_SHIFT; 89f9bbe447SVladimir Oltean } 90f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port); 91f9bbe447SVladimir Oltean 929c7caf28SVladimir Oltean bool vid_is_dsa_8021q_rxvlan(u16 vid) 939c7caf28SVladimir Oltean { 949c7caf28SVladimir Oltean return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_RX; 959c7caf28SVladimir Oltean } 969c7caf28SVladimir Oltean EXPORT_SYMBOL_GPL(vid_is_dsa_8021q_rxvlan); 979c7caf28SVladimir Oltean 989c7caf28SVladimir Oltean bool vid_is_dsa_8021q_txvlan(u16 vid) 999c7caf28SVladimir Oltean { 1009c7caf28SVladimir Oltean return (vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_TX; 1019c7caf28SVladimir Oltean } 1029c7caf28SVladimir Oltean EXPORT_SYMBOL_GPL(vid_is_dsa_8021q_txvlan); 1039c7caf28SVladimir Oltean 1041f66b0f0SVladimir Oltean bool vid_is_dsa_8021q(u16 vid) 1051f66b0f0SVladimir Oltean { 1069c7caf28SVladimir Oltean return vid_is_dsa_8021q_rxvlan(vid) || vid_is_dsa_8021q_txvlan(vid); 1071f66b0f0SVladimir Oltean } 1081f66b0f0SVladimir Oltean EXPORT_SYMBOL_GPL(vid_is_dsa_8021q); 1091f66b0f0SVladimir Oltean 1105f33183bSVladimir Oltean /* If @enabled is true, installs @vid with @flags into the switch port's HW 1115f33183bSVladimir Oltean * filter. 1125f33183bSVladimir Oltean * If @enabled is false, deletes @vid (ignores @flags) from the port. Had the 1135f33183bSVladimir Oltean * user explicitly configured this @vid through the bridge core, then the @vid 1145f33183bSVladimir Oltean * is installed again, but this time with the flags from the bridge layer. 1155f33183bSVladimir Oltean */ 116d7b1fd52SVladimir Oltean static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, 1175f33183bSVladimir Oltean u16 flags, bool enabled) 1185f33183bSVladimir Oltean { 119d7b1fd52SVladimir Oltean struct dsa_port *dp = dsa_to_port(ds, port); 1205f33183bSVladimir Oltean 1215f33183bSVladimir Oltean if (enabled) 1225da11eb4SVladimir Oltean return ds->ops->tag_8021q_vlan_add(ds, dp->index, vid, flags); 1235f33183bSVladimir Oltean 1245da11eb4SVladimir Oltean return ds->ops->tag_8021q_vlan_del(ds, dp->index, vid); 1255f33183bSVladimir Oltean } 1265f33183bSVladimir Oltean 127f9bbe447SVladimir Oltean /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single 128f9bbe447SVladimir Oltean * front-panel switch port (here swp0). 129f9bbe447SVladimir Oltean * 130f9bbe447SVladimir Oltean * Port identification through VLAN (802.1Q) tags has different requirements 131f9bbe447SVladimir Oltean * for it to work effectively: 132f9bbe447SVladimir Oltean * - On RX (ingress from network): each front-panel port must have a pvid 133f9bbe447SVladimir Oltean * that uniquely identifies it, and the egress of this pvid must be tagged 134f9bbe447SVladimir Oltean * towards the CPU port, so that software can recover the source port based 135f9bbe447SVladimir Oltean * on the VID in the frame. But this would only work for standalone ports; 136f9bbe447SVladimir Oltean * if bridged, this VLAN setup would break autonomous forwarding and would 137f9bbe447SVladimir Oltean * force all switched traffic to pass through the CPU. So we must also make 138f9bbe447SVladimir Oltean * the other front-panel ports members of this VID we're adding, albeit 139f9bbe447SVladimir Oltean * we're not making it their PVID (they'll still have their own). 140f9bbe447SVladimir Oltean * By the way - just because we're installing the same VID in multiple 141f9bbe447SVladimir Oltean * switch ports doesn't mean that they'll start to talk to one another, even 142f9bbe447SVladimir Oltean * while not bridged: the final forwarding decision is still an AND between 143f9bbe447SVladimir Oltean * the L2 forwarding information (which is limiting forwarding in this case) 144f9bbe447SVladimir Oltean * and the VLAN-based restrictions (of which there are none in this case, 145f9bbe447SVladimir Oltean * since all ports are members). 146f9bbe447SVladimir Oltean * - On TX (ingress from CPU and towards network) we are faced with a problem. 147f9bbe447SVladimir Oltean * If we were to tag traffic (from within DSA) with the port's pvid, all 148f9bbe447SVladimir Oltean * would be well, assuming the switch ports were standalone. Frames would 149f9bbe447SVladimir Oltean * have no choice but to be directed towards the correct front-panel port. 150f9bbe447SVladimir Oltean * But because we also want the RX VLAN to not break bridging, then 151f9bbe447SVladimir Oltean * inevitably that means that we have to give them a choice (of what 152f9bbe447SVladimir Oltean * front-panel port to go out on), and therefore we cannot steer traffic 153f9bbe447SVladimir Oltean * based on the RX VID. So what we do is simply install one more VID on the 154f9bbe447SVladimir Oltean * front-panel and CPU ports, and profit off of the fact that steering will 155f9bbe447SVladimir Oltean * work just by virtue of the fact that there is only one other port that's 156f9bbe447SVladimir Oltean * a member of the VID we're tagging the traffic with - the desired one. 157f9bbe447SVladimir Oltean * 158f9bbe447SVladimir Oltean * So at the end, each front-panel port will have one RX VID (also the PVID), 159f9bbe447SVladimir Oltean * the RX VID of all other front-panel ports, and one TX VID. Whereas the CPU 160f9bbe447SVladimir Oltean * port will have the RX and TX VIDs of all front-panel ports, and on top of 161f9bbe447SVladimir Oltean * that, is also tagged-input and tagged-output (VLAN trunk). 162f9bbe447SVladimir Oltean * 163f9bbe447SVladimir Oltean * CPU port CPU port 164f9bbe447SVladimir Oltean * +-------------+-----+-------------+ +-------------+-----+-------------+ 165f9bbe447SVladimir Oltean * | RX VID | | | | TX VID | | | 166f9bbe447SVladimir Oltean * | of swp0 | | | | of swp0 | | | 167f9bbe447SVladimir Oltean * | +-----+ | | +-----+ | 168f9bbe447SVladimir Oltean * | ^ T | | | Tagged | 169f9bbe447SVladimir Oltean * | | | | | ingress | 170f9bbe447SVladimir Oltean * | +-------+---+---+-------+ | | +-----------+ | 171f9bbe447SVladimir Oltean * | | | | | | | | Untagged | 172f9bbe447SVladimir Oltean * | | U v U v U v | | v egress | 173f9bbe447SVladimir Oltean * | +-----+ +-----+ +-----+ +-----+ | | +-----+ +-----+ +-----+ +-----+ | 174f9bbe447SVladimir Oltean * | | | | | | | | | | | | | | | | | | | | 175f9bbe447SVladimir Oltean * | |PVID | | | | | | | | | | | | | | | | | | 176f9bbe447SVladimir Oltean * +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+ 177f9bbe447SVladimir Oltean * swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3 178f9bbe447SVladimir Oltean */ 179d7b1fd52SVladimir Oltean static int dsa_8021q_setup_port(struct dsa_switch *ds, int port, bool enabled) 180f9bbe447SVladimir Oltean { 181d7b1fd52SVladimir Oltean struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; 182d7b1fd52SVladimir Oltean int upstream = dsa_upstream_port(ds, port); 183d7b1fd52SVladimir Oltean u16 rx_vid = dsa_8021q_rx_vid(ds, port); 184d7b1fd52SVladimir Oltean u16 tx_vid = dsa_8021q_tx_vid(ds, port); 185bbed0bbdSVladimir Oltean struct net_device *master; 1860fac6aa0SVladimir Oltean int i, err; 187f9bbe447SVladimir Oltean 188f9bbe447SVladimir Oltean /* The CPU port is implicitly configured by 189f9bbe447SVladimir Oltean * configuring the front-panel ports 190f9bbe447SVladimir Oltean */ 191d7b1fd52SVladimir Oltean if (!dsa_is_user_port(ds, port)) 192f9bbe447SVladimir Oltean return 0; 193f9bbe447SVladimir Oltean 194d7b1fd52SVladimir Oltean master = dsa_to_port(ds, port)->cpu_dp->master; 195bbed0bbdSVladimir Oltean 196f9bbe447SVladimir Oltean /* Add this user port's RX VID to the membership list of all others 197f9bbe447SVladimir Oltean * (including itself). This is so that bridging will not be hindered. 198f9bbe447SVladimir Oltean * L2 forwarding rules still take precedence when there are no VLAN 199f9bbe447SVladimir Oltean * restrictions, so there are no concerns about leaking traffic. 200f9bbe447SVladimir Oltean */ 201d7b1fd52SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 202f9bbe447SVladimir Oltean u16 flags; 203f9bbe447SVladimir Oltean 204f9bbe447SVladimir Oltean if (i == upstream) 205d34d2baaSIoana Ciornei continue; 206f9bbe447SVladimir Oltean else if (i == port) 207f9bbe447SVladimir Oltean /* The RX VID is pvid on this port */ 208f9bbe447SVladimir Oltean flags = BRIDGE_VLAN_INFO_UNTAGGED | 209f9bbe447SVladimir Oltean BRIDGE_VLAN_INFO_PVID; 210f9bbe447SVladimir Oltean else 211f9bbe447SVladimir Oltean /* The RX VID is a regular VLAN on all others */ 212f9bbe447SVladimir Oltean flags = BRIDGE_VLAN_INFO_UNTAGGED; 213f9bbe447SVladimir Oltean 214d7b1fd52SVladimir Oltean err = dsa_8021q_vid_apply(ds, i, rx_vid, flags, enabled); 215f9bbe447SVladimir Oltean if (err) { 216d7b1fd52SVladimir Oltean dev_err(ds->dev, 21769ebb370SVladimir Oltean "Failed to apply RX VID %d to port %d: %pe\n", 21869ebb370SVladimir Oltean rx_vid, port, ERR_PTR(err)); 219f9bbe447SVladimir Oltean return err; 220f9bbe447SVladimir Oltean } 221f9bbe447SVladimir Oltean } 222d34d2baaSIoana Ciornei 223d34d2baaSIoana Ciornei /* CPU port needs to see this port's RX VID 224d34d2baaSIoana Ciornei * as tagged egress. 225d34d2baaSIoana Ciornei */ 226d7b1fd52SVladimir Oltean err = dsa_8021q_vid_apply(ds, upstream, rx_vid, 0, enabled); 227d34d2baaSIoana Ciornei if (err) { 228d7b1fd52SVladimir Oltean dev_err(ds->dev, 22969ebb370SVladimir Oltean "Failed to apply RX VID %d to port %d: %pe\n", 23069ebb370SVladimir Oltean rx_vid, port, ERR_PTR(err)); 231d34d2baaSIoana Ciornei return err; 232d34d2baaSIoana Ciornei } 233d34d2baaSIoana Ciornei 2340fac6aa0SVladimir Oltean /* Add @rx_vid to the master's RX filter. */ 235bbed0bbdSVladimir Oltean if (enabled) 2360fac6aa0SVladimir Oltean vlan_vid_add(master, ctx->proto, rx_vid); 237bbed0bbdSVladimir Oltean else 2380fac6aa0SVladimir Oltean vlan_vid_del(master, ctx->proto, rx_vid); 239bbed0bbdSVladimir Oltean 240f9bbe447SVladimir Oltean /* Finally apply the TX VID on this port and on the CPU port */ 241d7b1fd52SVladimir Oltean err = dsa_8021q_vid_apply(ds, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED, 2425f33183bSVladimir Oltean enabled); 243f9bbe447SVladimir Oltean if (err) { 244d7b1fd52SVladimir Oltean dev_err(ds->dev, 24569ebb370SVladimir Oltean "Failed to apply TX VID %d on port %d: %pe\n", 24669ebb370SVladimir Oltean tx_vid, port, ERR_PTR(err)); 247f9bbe447SVladimir Oltean return err; 248f9bbe447SVladimir Oltean } 249d7b1fd52SVladimir Oltean err = dsa_8021q_vid_apply(ds, upstream, tx_vid, 0, enabled); 250f9bbe447SVladimir Oltean if (err) { 251d7b1fd52SVladimir Oltean dev_err(ds->dev, 25269ebb370SVladimir Oltean "Failed to apply TX VID %d on port %d: %pe\n", 25369ebb370SVladimir Oltean tx_vid, upstream, ERR_PTR(err)); 254f9bbe447SVladimir Oltean return err; 255f9bbe447SVladimir Oltean } 256f9bbe447SVladimir Oltean 2575f33183bSVladimir Oltean return err; 258f9bbe447SVladimir Oltean } 2597e092af2SVladimir Oltean 260*328621f6SVladimir Oltean static int dsa_8021q_setup(struct dsa_switch *ds, bool enabled) 2617e092af2SVladimir Oltean { 262a81a4574SVladimir Oltean int err, port; 2637e092af2SVladimir Oltean 264bbed0bbdSVladimir Oltean ASSERT_RTNL(); 265bbed0bbdSVladimir Oltean 266d7b1fd52SVladimir Oltean for (port = 0; port < ds->num_ports; port++) { 267d7b1fd52SVladimir Oltean err = dsa_8021q_setup_port(ds, port, enabled); 268a81a4574SVladimir Oltean if (err < 0) { 269d7b1fd52SVladimir Oltean dev_err(ds->dev, 27069ebb370SVladimir Oltean "Failed to setup VLAN tagging for port %d: %pe\n", 27169ebb370SVladimir Oltean port, ERR_PTR(err)); 272a81a4574SVladimir Oltean return err; 2737e092af2SVladimir Oltean } 2747e092af2SVladimir Oltean } 2757e092af2SVladimir Oltean 2767e092af2SVladimir Oltean return 0; 2777e092af2SVladimir Oltean } 278f9bbe447SVladimir Oltean 279d7b1fd52SVladimir Oltean static int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port, 280d7b1fd52SVladimir Oltean struct dsa_switch *other_ds, 281ac02a451SVladimir Oltean int other_port, bool enabled) 282ac02a451SVladimir Oltean { 283d7b1fd52SVladimir Oltean u16 rx_vid = dsa_8021q_rx_vid(ds, port); 284ac02a451SVladimir Oltean 285ac02a451SVladimir Oltean /* @rx_vid of local @ds port @port goes to @other_port of 286ac02a451SVladimir Oltean * @other_ds 287ac02a451SVladimir Oltean */ 288d7b1fd52SVladimir Oltean return dsa_8021q_vid_apply(other_ds, other_port, rx_vid, 289ac02a451SVladimir Oltean BRIDGE_VLAN_INFO_UNTAGGED, enabled); 290ac02a451SVladimir Oltean } 291ac02a451SVladimir Oltean 292d7b1fd52SVladimir Oltean static int dsa_8021q_crosschip_link_add(struct dsa_switch *ds, int port, 293d7b1fd52SVladimir Oltean struct dsa_switch *other_ds, 2945899ee36SVladimir Oltean int other_port) 295ac02a451SVladimir Oltean { 296d7b1fd52SVladimir Oltean struct dsa_8021q_context *other_ctx = other_ds->tag_8021q_ctx; 297d7b1fd52SVladimir Oltean struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; 298ac02a451SVladimir Oltean struct dsa_8021q_crosschip_link *c; 299ac02a451SVladimir Oltean 3005899ee36SVladimir Oltean list_for_each_entry(c, &ctx->crosschip_links, list) { 3015899ee36SVladimir Oltean if (c->port == port && c->other_ctx == other_ctx && 302ac02a451SVladimir Oltean c->other_port == other_port) { 303ac02a451SVladimir Oltean refcount_inc(&c->refcount); 304ac02a451SVladimir Oltean return 0; 305ac02a451SVladimir Oltean } 306ac02a451SVladimir Oltean } 307ac02a451SVladimir Oltean 308d7b1fd52SVladimir Oltean dev_dbg(ds->dev, 3095899ee36SVladimir Oltean "adding crosschip link from port %d to %s port %d\n", 310d7b1fd52SVladimir Oltean port, dev_name(other_ds->dev), other_port); 311ac02a451SVladimir Oltean 312ac02a451SVladimir Oltean c = kzalloc(sizeof(*c), GFP_KERNEL); 313ac02a451SVladimir Oltean if (!c) 314ac02a451SVladimir Oltean return -ENOMEM; 315ac02a451SVladimir Oltean 316ac02a451SVladimir Oltean c->port = port; 3175899ee36SVladimir Oltean c->other_ctx = other_ctx; 318ac02a451SVladimir Oltean c->other_port = other_port; 319ac02a451SVladimir Oltean refcount_set(&c->refcount, 1); 320ac02a451SVladimir Oltean 3215899ee36SVladimir Oltean list_add(&c->list, &ctx->crosschip_links); 322ac02a451SVladimir Oltean 323ac02a451SVladimir Oltean return 0; 324ac02a451SVladimir Oltean } 325ac02a451SVladimir Oltean 326d7b1fd52SVladimir Oltean static void dsa_8021q_crosschip_link_del(struct dsa_switch *ds, 327ac02a451SVladimir Oltean struct dsa_8021q_crosschip_link *c, 328ac02a451SVladimir Oltean bool *keep) 329ac02a451SVladimir Oltean { 330ac02a451SVladimir Oltean *keep = !refcount_dec_and_test(&c->refcount); 331ac02a451SVladimir Oltean 332ac02a451SVladimir Oltean if (*keep) 333ac02a451SVladimir Oltean return; 334ac02a451SVladimir Oltean 335d7b1fd52SVladimir Oltean dev_dbg(ds->dev, 336ac02a451SVladimir Oltean "deleting crosschip link from port %d to %s port %d\n", 3375899ee36SVladimir Oltean c->port, dev_name(c->other_ctx->ds->dev), c->other_port); 338ac02a451SVladimir Oltean 339ac02a451SVladimir Oltean list_del(&c->list); 340ac02a451SVladimir Oltean kfree(c); 341ac02a451SVladimir Oltean } 342ac02a451SVladimir Oltean 343ac02a451SVladimir Oltean /* Make traffic from local port @port be received by remote port @other_port. 344ac02a451SVladimir Oltean * This means that our @rx_vid needs to be installed on @other_ds's upstream 345ac02a451SVladimir Oltean * and user ports. The user ports should be egress-untagged so that they can 346ac02a451SVladimir Oltean * pop the dsa_8021q VLAN. But the @other_upstream can be either egress-tagged 347ac02a451SVladimir Oltean * or untagged: it doesn't matter, since it should never egress a frame having 348ac02a451SVladimir Oltean * our @rx_vid. 349ac02a451SVladimir Oltean */ 350d7b1fd52SVladimir Oltean int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port, 351d7b1fd52SVladimir Oltean struct dsa_switch *other_ds, 3525899ee36SVladimir Oltean int other_port) 353ac02a451SVladimir Oltean { 354ac02a451SVladimir Oltean /* @other_upstream is how @other_ds reaches us. If we are part 355ac02a451SVladimir Oltean * of disjoint trees, then we are probably connected through 356ac02a451SVladimir Oltean * our CPU ports. If we're part of the same tree though, we should 357ac02a451SVladimir Oltean * probably use dsa_towards_port. 358ac02a451SVladimir Oltean */ 359d7b1fd52SVladimir Oltean int other_upstream = dsa_upstream_port(other_ds, other_port); 360a81a4574SVladimir Oltean int err; 361ac02a451SVladimir Oltean 362d7b1fd52SVladimir Oltean err = dsa_8021q_crosschip_link_add(ds, port, other_ds, other_port); 363a81a4574SVladimir Oltean if (err) 364a81a4574SVladimir Oltean return err; 365ac02a451SVladimir Oltean 366d7b1fd52SVladimir Oltean err = dsa_8021q_crosschip_link_apply(ds, port, other_ds, 367ac02a451SVladimir Oltean other_port, true); 368a81a4574SVladimir Oltean if (err) 369a81a4574SVladimir Oltean return err; 370ac02a451SVladimir Oltean 371d7b1fd52SVladimir Oltean err = dsa_8021q_crosschip_link_add(ds, port, other_ds, other_upstream); 372a81a4574SVladimir Oltean if (err) 373a81a4574SVladimir Oltean return err; 374ac02a451SVladimir Oltean 375d7b1fd52SVladimir Oltean return dsa_8021q_crosschip_link_apply(ds, port, other_ds, 376ac02a451SVladimir Oltean other_upstream, true); 377ac02a451SVladimir Oltean } 378ac02a451SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_crosschip_bridge_join); 379ac02a451SVladimir Oltean 380d7b1fd52SVladimir Oltean int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port, 381d7b1fd52SVladimir Oltean struct dsa_switch *other_ds, 3825899ee36SVladimir Oltean int other_port) 383ac02a451SVladimir Oltean { 384d7b1fd52SVladimir Oltean struct dsa_8021q_context *other_ctx = other_ds->tag_8021q_ctx; 385d7b1fd52SVladimir Oltean int other_upstream = dsa_upstream_port(other_ds, other_port); 386d7b1fd52SVladimir Oltean struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; 387ac02a451SVladimir Oltean struct dsa_8021q_crosschip_link *c, *n; 388ac02a451SVladimir Oltean 3895899ee36SVladimir Oltean list_for_each_entry_safe(c, n, &ctx->crosschip_links, list) { 3905899ee36SVladimir Oltean if (c->port == port && c->other_ctx == other_ctx && 391ac02a451SVladimir Oltean (c->other_port == other_port || 392ac02a451SVladimir Oltean c->other_port == other_upstream)) { 393ac02a451SVladimir Oltean int other_port = c->other_port; 394ac02a451SVladimir Oltean bool keep; 395a81a4574SVladimir Oltean int err; 396ac02a451SVladimir Oltean 397d7b1fd52SVladimir Oltean dsa_8021q_crosschip_link_del(ds, c, &keep); 398ac02a451SVladimir Oltean if (keep) 399ac02a451SVladimir Oltean continue; 400ac02a451SVladimir Oltean 401d7b1fd52SVladimir Oltean err = dsa_8021q_crosschip_link_apply(ds, port, 402d7b1fd52SVladimir Oltean other_ds, 403ac02a451SVladimir Oltean other_port, 404ac02a451SVladimir Oltean false); 405a81a4574SVladimir Oltean if (err) 406a81a4574SVladimir Oltean return err; 407ac02a451SVladimir Oltean } 408ac02a451SVladimir Oltean } 409ac02a451SVladimir Oltean 410ac02a451SVladimir Oltean return 0; 411ac02a451SVladimir Oltean } 412ac02a451SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_crosschip_bridge_leave); 413ac02a451SVladimir Oltean 4145da11eb4SVladimir Oltean int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto) 415cedf4670SVladimir Oltean { 416cedf4670SVladimir Oltean struct dsa_8021q_context *ctx; 417cedf4670SVladimir Oltean 418cedf4670SVladimir Oltean ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 419cedf4670SVladimir Oltean if (!ctx) 420d7b1fd52SVladimir Oltean return -ENOMEM; 421cedf4670SVladimir Oltean 422cedf4670SVladimir Oltean ctx->proto = proto; 423cedf4670SVladimir Oltean ctx->ds = ds; 424cedf4670SVladimir Oltean 425cedf4670SVladimir Oltean INIT_LIST_HEAD(&ctx->crosschip_links); 426cedf4670SVladimir Oltean 427d7b1fd52SVladimir Oltean ds->tag_8021q_ctx = ctx; 428d7b1fd52SVladimir Oltean 429*328621f6SVladimir Oltean return dsa_8021q_setup(ds, true); 430cedf4670SVladimir Oltean } 431cedf4670SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_register); 432cedf4670SVladimir Oltean 433d7b1fd52SVladimir Oltean void dsa_tag_8021q_unregister(struct dsa_switch *ds) 434cedf4670SVladimir Oltean { 435d7b1fd52SVladimir Oltean struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; 436cedf4670SVladimir Oltean struct dsa_8021q_crosschip_link *c, *n; 437*328621f6SVladimir Oltean int err; 438*328621f6SVladimir Oltean 439*328621f6SVladimir Oltean err = dsa_8021q_setup(ds, false); 440*328621f6SVladimir Oltean if (err) 441*328621f6SVladimir Oltean dev_err(ds->dev, "failed to tear down tag_8021q VLANs: %pe\n", 442*328621f6SVladimir Oltean ERR_PTR(err)); 443cedf4670SVladimir Oltean 444cedf4670SVladimir Oltean list_for_each_entry_safe(c, n, &ctx->crosschip_links, list) { 445cedf4670SVladimir Oltean list_del(&c->list); 446cedf4670SVladimir Oltean kfree(c); 447cedf4670SVladimir Oltean } 448cedf4670SVladimir Oltean 449d7b1fd52SVladimir Oltean ds->tag_8021q_ctx = NULL; 450d7b1fd52SVladimir Oltean 451cedf4670SVladimir Oltean kfree(ctx); 452cedf4670SVladimir Oltean } 453cedf4670SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_unregister); 454cedf4670SVladimir Oltean 455f9bbe447SVladimir Oltean struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, 456f9bbe447SVladimir Oltean u16 tpid, u16 tci) 457f9bbe447SVladimir Oltean { 458f9bbe447SVladimir Oltean /* skb->data points at skb_mac_header, which 459f9bbe447SVladimir Oltean * is fine for vlan_insert_tag. 460f9bbe447SVladimir Oltean */ 461f9bbe447SVladimir Oltean return vlan_insert_tag(skb, htons(tpid), tci); 462f9bbe447SVladimir Oltean } 463f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_xmit); 464f9bbe447SVladimir Oltean 4650fac6aa0SVladimir Oltean void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id) 466233697b3SVladimir Oltean { 467233697b3SVladimir Oltean u16 vid, tci; 468233697b3SVladimir Oltean 469233697b3SVladimir Oltean skb_push_rcsum(skb, ETH_HLEN); 470233697b3SVladimir Oltean if (skb_vlan_tag_present(skb)) { 471233697b3SVladimir Oltean tci = skb_vlan_tag_get(skb); 472233697b3SVladimir Oltean __vlan_hwaccel_clear_tag(skb); 473233697b3SVladimir Oltean } else { 474233697b3SVladimir Oltean __skb_vlan_pop(skb, &tci); 475233697b3SVladimir Oltean } 476233697b3SVladimir Oltean skb_pull_rcsum(skb, ETH_HLEN); 477233697b3SVladimir Oltean 478233697b3SVladimir Oltean vid = tci & VLAN_VID_MASK; 479233697b3SVladimir Oltean 480233697b3SVladimir Oltean *source_port = dsa_8021q_rx_source_port(vid); 481233697b3SVladimir Oltean *switch_id = dsa_8021q_rx_switch_id(vid); 482233697b3SVladimir Oltean skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; 483233697b3SVladimir Oltean } 484233697b3SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rcv); 485