xref: /openbmc/linux/net/dsa/tag_8021q.c (revision 328621f6)
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