xref: /openbmc/linux/net/dsa/tag_8021q.c (revision f0a9d563)
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
5b18e04e3SVladimir Oltean  * primitives for taggers that rely on 802.1Q VLAN tags to use.
6f9bbe447SVladimir Oltean  */
7f9bbe447SVladimir Oltean #include <linux/if_vlan.h>
8ac02a451SVladimir Oltean #include <linux/dsa/8021q.h>
9f9bbe447SVladimir Oltean 
10022bba63SVladimir Oltean #include "port.h"
110c603136SVladimir Oltean #include "switch.h"
12bd954b82SVladimir Oltean #include "tag.h"
1319d05ea7SVladimir Oltean #include "tag_8021q.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  * +-----------+-----+-----------------+-----------+-----------------------+
2004b67e18SVladimir Oltean  * |    RSV    | VBID|    SWITCH_ID    |   VBID    |          PORT         |
210471dd42SVladimir Oltean  * +-----------+-----+-----------------+-----------+-----------------------+
220471dd42SVladimir Oltean  *
2304b67e18SVladimir Oltean  * RSV - VID[11:10]:
2404b67e18SVladimir Oltean  *	Reserved. Must be set to 3 (0b11).
250471dd42SVladimir Oltean  *
260471dd42SVladimir Oltean  * SWITCH_ID - VID[8:6]:
27fcee85f1SVivien Didelot  *	Index of switch within DSA tree. Must be between 0 and 7.
280471dd42SVladimir Oltean  *
29b6ad86e6SVladimir Oltean  * VBID - { VID[9], VID[5:4] }:
30b6ad86e6SVladimir Oltean  *	Virtual bridge ID. If between 1 and 7, packet targets the broadcast
31b6ad86e6SVladimir Oltean  *	domain of a bridge. If transmitted as zero, packet targets a single
32d7f9787aSVladimir Oltean  *	port.
330fac6aa0SVladimir Oltean  *
340471dd42SVladimir Oltean  * PORT - VID[3:0]:
35fcee85f1SVivien Didelot  *	Index of switch port. Must be between 0 and 15.
36f9bbe447SVladimir Oltean  */
370471dd42SVladimir Oltean 
3804b67e18SVladimir Oltean #define DSA_8021Q_RSV_VAL		3
3904b67e18SVladimir Oltean #define DSA_8021Q_RSV_SHIFT		10
4004b67e18SVladimir Oltean #define DSA_8021Q_RSV_MASK		GENMASK(11, 10)
4104b67e18SVladimir Oltean #define DSA_8021Q_RSV			((DSA_8021Q_RSV_VAL << DSA_8021Q_RSV_SHIFT) & \
4204b67e18SVladimir Oltean 							       DSA_8021Q_RSV_MASK)
430471dd42SVladimir Oltean 
440471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_SHIFT	6
450471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_MASK	GENMASK(8, 6)
460471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID(x)		(((x) << DSA_8021Q_SWITCH_ID_SHIFT) & \
470471dd42SVladimir Oltean 						 DSA_8021Q_SWITCH_ID_MASK)
480471dd42SVladimir Oltean 
49b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID_HI_SHIFT		9
50b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID_HI_MASK		GENMASK(9, 9)
51b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID_LO_SHIFT		4
52b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID_LO_MASK		GENMASK(5, 4)
53b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID_HI(x)		(((x) & GENMASK(2, 2)) >> 2)
54b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID_LO(x)		((x) & GENMASK(1, 0))
55b6ad86e6SVladimir Oltean #define DSA_8021Q_VBID(x)		\
56b6ad86e6SVladimir Oltean 		(((DSA_8021Q_VBID_LO(x) << DSA_8021Q_VBID_LO_SHIFT) & \
57b6ad86e6SVladimir Oltean 		  DSA_8021Q_VBID_LO_MASK) | \
58b6ad86e6SVladimir Oltean 		 ((DSA_8021Q_VBID_HI(x) << DSA_8021Q_VBID_HI_SHIFT) & \
59b6ad86e6SVladimir Oltean 		  DSA_8021Q_VBID_HI_MASK))
60b6ad86e6SVladimir Oltean 
610471dd42SVladimir Oltean #define DSA_8021Q_PORT_SHIFT		0
620471dd42SVladimir Oltean #define DSA_8021Q_PORT_MASK		GENMASK(3, 0)
630471dd42SVladimir Oltean #define DSA_8021Q_PORT(x)		(((x) << DSA_8021Q_PORT_SHIFT) & \
640471dd42SVladimir Oltean 						 DSA_8021Q_PORT_MASK)
65f9bbe447SVladimir Oltean 
6619d05ea7SVladimir Oltean struct dsa_tag_8021q_vlan {
6719d05ea7SVladimir Oltean 	struct list_head list;
6819d05ea7SVladimir Oltean 	int port;
6919d05ea7SVladimir Oltean 	u16 vid;
7019d05ea7SVladimir Oltean 	refcount_t refcount;
7119d05ea7SVladimir Oltean };
7219d05ea7SVladimir Oltean 
7319d05ea7SVladimir Oltean struct dsa_8021q_context {
7419d05ea7SVladimir Oltean 	struct dsa_switch *ds;
7519d05ea7SVladimir Oltean 	struct list_head vlans;
7619d05ea7SVladimir Oltean 	/* EtherType of RX VID, used for filtering on master interface */
7719d05ea7SVladimir Oltean 	__be16 proto;
7819d05ea7SVladimir Oltean };
7919d05ea7SVladimir Oltean 
dsa_tag_8021q_bridge_vid(unsigned int bridge_num)80b6362bdfSVladimir Oltean u16 dsa_tag_8021q_bridge_vid(unsigned int bridge_num)
81b6ad86e6SVladimir Oltean {
823f9bb030SVladimir Oltean 	/* The VBID value of 0 is reserved for precise TX, but it is also
833f9bb030SVladimir Oltean 	 * reserved/invalid for the bridge_num, so all is well.
843f9bb030SVladimir Oltean 	 */
8504b67e18SVladimir Oltean 	return DSA_8021Q_RSV | DSA_8021Q_VBID(bridge_num);
86b6ad86e6SVladimir Oltean }
87b6362bdfSVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_vid);
88b6ad86e6SVladimir Oltean 
89f9bbe447SVladimir Oltean /* Returns the VID that will be installed as pvid for this switch port, sent as
90f9bbe447SVladimir Oltean  * tagged egress towards the CPU port and decoded by the rcv function.
91f9bbe447SVladimir Oltean  */
dsa_tag_8021q_standalone_vid(const struct dsa_port * dp)9204b67e18SVladimir Oltean u16 dsa_tag_8021q_standalone_vid(const struct dsa_port *dp)
93f9bbe447SVladimir Oltean {
9404b67e18SVladimir Oltean 	return DSA_8021Q_RSV | DSA_8021Q_SWITCH_ID(dp->ds->index) |
95992e5cc7SVladimir Oltean 	       DSA_8021Q_PORT(dp->index);
96f9bbe447SVladimir Oltean }
9704b67e18SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_standalone_vid);
98f9bbe447SVladimir Oltean 
99f9bbe447SVladimir Oltean /* Returns the decoded switch ID from the RX VID. */
dsa_8021q_rx_switch_id(u16 vid)100f9bbe447SVladimir Oltean int dsa_8021q_rx_switch_id(u16 vid)
101f9bbe447SVladimir Oltean {
1020471dd42SVladimir Oltean 	return (vid & DSA_8021Q_SWITCH_ID_MASK) >> DSA_8021Q_SWITCH_ID_SHIFT;
103f9bbe447SVladimir Oltean }
104f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_switch_id);
105f9bbe447SVladimir Oltean 
106f9bbe447SVladimir Oltean /* Returns the decoded port ID from the RX VID. */
dsa_8021q_rx_source_port(u16 vid)107f9bbe447SVladimir Oltean int dsa_8021q_rx_source_port(u16 vid)
108f9bbe447SVladimir Oltean {
1090471dd42SVladimir Oltean 	return (vid & DSA_8021Q_PORT_MASK) >> DSA_8021Q_PORT_SHIFT;
110f9bbe447SVladimir Oltean }
111f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port);
112f9bbe447SVladimir Oltean 
11391495f21SVladimir Oltean /* Returns the decoded VBID from the RX VID. */
dsa_tag_8021q_rx_vbid(u16 vid)11491495f21SVladimir Oltean static int dsa_tag_8021q_rx_vbid(u16 vid)
11591495f21SVladimir Oltean {
11691495f21SVladimir Oltean 	u16 vbid_hi = (vid & DSA_8021Q_VBID_HI_MASK) >> DSA_8021Q_VBID_HI_SHIFT;
11791495f21SVladimir Oltean 	u16 vbid_lo = (vid & DSA_8021Q_VBID_LO_MASK) >> DSA_8021Q_VBID_LO_SHIFT;
11891495f21SVladimir Oltean 
11991495f21SVladimir Oltean 	return (vbid_hi << 2) | vbid_lo;
12091495f21SVladimir Oltean }
12191495f21SVladimir Oltean 
vid_is_dsa_8021q(u16 vid)1221f66b0f0SVladimir Oltean bool vid_is_dsa_8021q(u16 vid)
1231f66b0f0SVladimir Oltean {
12404b67e18SVladimir Oltean 	u16 rsv = (vid & DSA_8021Q_RSV_MASK) >> DSA_8021Q_RSV_SHIFT;
12504b67e18SVladimir Oltean 
12604b67e18SVladimir Oltean 	return rsv == DSA_8021Q_RSV_VAL;
1271f66b0f0SVladimir Oltean }
1281f66b0f0SVladimir Oltean EXPORT_SYMBOL_GPL(vid_is_dsa_8021q);
1291f66b0f0SVladimir Oltean 
130c64b9c05SVladimir Oltean static struct dsa_tag_8021q_vlan *
dsa_tag_8021q_vlan_find(struct dsa_8021q_context * ctx,int port,u16 vid)131c64b9c05SVladimir Oltean dsa_tag_8021q_vlan_find(struct dsa_8021q_context *ctx, int port, u16 vid)
1325f33183bSVladimir Oltean {
133c64b9c05SVladimir Oltean 	struct dsa_tag_8021q_vlan *v;
134c64b9c05SVladimir Oltean 
135c64b9c05SVladimir Oltean 	list_for_each_entry(v, &ctx->vlans, list)
136c64b9c05SVladimir Oltean 		if (v->vid == vid && v->port == port)
137c64b9c05SVladimir Oltean 			return v;
138c64b9c05SVladimir Oltean 
139c64b9c05SVladimir Oltean 	return NULL;
140c64b9c05SVladimir Oltean }
141c64b9c05SVladimir Oltean 
dsa_port_do_tag_8021q_vlan_add(struct dsa_port * dp,u16 vid,u16 flags)142fac6abd5SVladimir Oltean static int dsa_port_do_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid,
143fac6abd5SVladimir Oltean 					  u16 flags)
144c64b9c05SVladimir Oltean {
145fac6abd5SVladimir Oltean 	struct dsa_8021q_context *ctx = dp->ds->tag_8021q_ctx;
146fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
147c64b9c05SVladimir Oltean 	struct dsa_tag_8021q_vlan *v;
148fac6abd5SVladimir Oltean 	int port = dp->index;
149c64b9c05SVladimir Oltean 	int err;
1505f33183bSVladimir Oltean 
151c64b9c05SVladimir Oltean 	/* No need to bother with refcounting for user ports */
152c64b9c05SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
153c64b9c05SVladimir Oltean 		return ds->ops->tag_8021q_vlan_add(ds, port, vid, flags);
1545f33183bSVladimir Oltean 
155c64b9c05SVladimir Oltean 	v = dsa_tag_8021q_vlan_find(ctx, port, vid);
156c64b9c05SVladimir Oltean 	if (v) {
157c64b9c05SVladimir Oltean 		refcount_inc(&v->refcount);
158c64b9c05SVladimir Oltean 		return 0;
159c64b9c05SVladimir Oltean 	}
160c64b9c05SVladimir Oltean 
161c64b9c05SVladimir Oltean 	v = kzalloc(sizeof(*v), GFP_KERNEL);
162c64b9c05SVladimir Oltean 	if (!v)
163c64b9c05SVladimir Oltean 		return -ENOMEM;
164c64b9c05SVladimir Oltean 
165c64b9c05SVladimir Oltean 	err = ds->ops->tag_8021q_vlan_add(ds, port, vid, flags);
166c64b9c05SVladimir Oltean 	if (err) {
167c64b9c05SVladimir Oltean 		kfree(v);
168c64b9c05SVladimir Oltean 		return err;
169c64b9c05SVladimir Oltean 	}
170c64b9c05SVladimir Oltean 
171c64b9c05SVladimir Oltean 	v->vid = vid;
172c64b9c05SVladimir Oltean 	v->port = port;
173c64b9c05SVladimir Oltean 	refcount_set(&v->refcount, 1);
174c64b9c05SVladimir Oltean 	list_add_tail(&v->list, &ctx->vlans);
175c64b9c05SVladimir Oltean 
176c64b9c05SVladimir Oltean 	return 0;
177c64b9c05SVladimir Oltean }
178c64b9c05SVladimir Oltean 
dsa_port_do_tag_8021q_vlan_del(struct dsa_port * dp,u16 vid)179fac6abd5SVladimir Oltean static int dsa_port_do_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid)
180c64b9c05SVladimir Oltean {
181fac6abd5SVladimir Oltean 	struct dsa_8021q_context *ctx = dp->ds->tag_8021q_ctx;
182fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
183c64b9c05SVladimir Oltean 	struct dsa_tag_8021q_vlan *v;
184fac6abd5SVladimir Oltean 	int port = dp->index;
185c64b9c05SVladimir Oltean 	int err;
186c64b9c05SVladimir Oltean 
187c64b9c05SVladimir Oltean 	/* No need to bother with refcounting for user ports */
188c64b9c05SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
189c64b9c05SVladimir Oltean 		return ds->ops->tag_8021q_vlan_del(ds, port, vid);
190c64b9c05SVladimir Oltean 
191c64b9c05SVladimir Oltean 	v = dsa_tag_8021q_vlan_find(ctx, port, vid);
192c64b9c05SVladimir Oltean 	if (!v)
193c64b9c05SVladimir Oltean 		return -ENOENT;
194c64b9c05SVladimir Oltean 
195c64b9c05SVladimir Oltean 	if (!refcount_dec_and_test(&v->refcount))
196c64b9c05SVladimir Oltean 		return 0;
197c64b9c05SVladimir Oltean 
198c64b9c05SVladimir Oltean 	err = ds->ops->tag_8021q_vlan_del(ds, port, vid);
199c64b9c05SVladimir Oltean 	if (err) {
200c64b9c05SVladimir Oltean 		refcount_inc(&v->refcount);
201c64b9c05SVladimir Oltean 		return err;
202c64b9c05SVladimir Oltean 	}
203c64b9c05SVladimir Oltean 
204c64b9c05SVladimir Oltean 	list_del(&v->list);
205c64b9c05SVladimir Oltean 	kfree(v);
206c64b9c05SVladimir Oltean 
207c64b9c05SVladimir Oltean 	return 0;
208c64b9c05SVladimir Oltean }
209c64b9c05SVladimir Oltean 
210c64b9c05SVladimir Oltean static bool
dsa_port_tag_8021q_vlan_match(struct dsa_port * dp,struct dsa_notifier_tag_8021q_vlan_info * info)211fac6abd5SVladimir Oltean dsa_port_tag_8021q_vlan_match(struct dsa_port *dp,
212c64b9c05SVladimir Oltean 			      struct dsa_notifier_tag_8021q_vlan_info *info)
213c64b9c05SVladimir Oltean {
214726816a1SVladimir Oltean 	return dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp) || dp == info->dp;
215c64b9c05SVladimir Oltean }
216c64b9c05SVladimir Oltean 
dsa_switch_tag_8021q_vlan_add(struct dsa_switch * ds,struct dsa_notifier_tag_8021q_vlan_info * info)217c64b9c05SVladimir Oltean int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,
218c64b9c05SVladimir Oltean 				  struct dsa_notifier_tag_8021q_vlan_info *info)
219c64b9c05SVladimir Oltean {
220fac6abd5SVladimir Oltean 	struct dsa_port *dp;
221fac6abd5SVladimir Oltean 	int err;
222c64b9c05SVladimir Oltean 
223c64b9c05SVladimir Oltean 	/* Since we use dsa_broadcast(), there might be other switches in other
224c64b9c05SVladimir Oltean 	 * trees which don't support tag_8021q, so don't return an error.
225c64b9c05SVladimir Oltean 	 * Or they might even support tag_8021q but have not registered yet to
226c64b9c05SVladimir Oltean 	 * use it (maybe they use another tagger currently).
227c64b9c05SVladimir Oltean 	 */
228c64b9c05SVladimir Oltean 	if (!ds->ops->tag_8021q_vlan_add || !ds->tag_8021q_ctx)
229c64b9c05SVladimir Oltean 		return 0;
230c64b9c05SVladimir Oltean 
231fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
232fac6abd5SVladimir Oltean 		if (dsa_port_tag_8021q_vlan_match(dp, info)) {
233c64b9c05SVladimir Oltean 			u16 flags = 0;
234c64b9c05SVladimir Oltean 
235fac6abd5SVladimir Oltean 			if (dsa_port_is_user(dp))
23604b67e18SVladimir Oltean 				flags |= BRIDGE_VLAN_INFO_UNTAGGED |
23704b67e18SVladimir Oltean 					 BRIDGE_VLAN_INFO_PVID;
23891495f21SVladimir Oltean 
239fac6abd5SVladimir Oltean 			err = dsa_port_do_tag_8021q_vlan_add(dp, info->vid,
240c64b9c05SVladimir Oltean 							     flags);
241c64b9c05SVladimir Oltean 			if (err)
242c64b9c05SVladimir Oltean 				return err;
243c64b9c05SVladimir Oltean 		}
244c64b9c05SVladimir Oltean 	}
245c64b9c05SVladimir Oltean 
246c64b9c05SVladimir Oltean 	return 0;
247c64b9c05SVladimir Oltean }
248c64b9c05SVladimir Oltean 
dsa_switch_tag_8021q_vlan_del(struct dsa_switch * ds,struct dsa_notifier_tag_8021q_vlan_info * info)249c64b9c05SVladimir Oltean int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,
250c64b9c05SVladimir Oltean 				  struct dsa_notifier_tag_8021q_vlan_info *info)
251c64b9c05SVladimir Oltean {
252fac6abd5SVladimir Oltean 	struct dsa_port *dp;
253fac6abd5SVladimir Oltean 	int err;
254c64b9c05SVladimir Oltean 
255c64b9c05SVladimir Oltean 	if (!ds->ops->tag_8021q_vlan_del || !ds->tag_8021q_ctx)
256c64b9c05SVladimir Oltean 		return 0;
257c64b9c05SVladimir Oltean 
258fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
259fac6abd5SVladimir Oltean 		if (dsa_port_tag_8021q_vlan_match(dp, info)) {
260fac6abd5SVladimir Oltean 			err = dsa_port_do_tag_8021q_vlan_del(dp, info->vid);
261c64b9c05SVladimir Oltean 			if (err)
262c64b9c05SVladimir Oltean 				return err;
263c64b9c05SVladimir Oltean 		}
264c64b9c05SVladimir Oltean 	}
265c64b9c05SVladimir Oltean 
266c64b9c05SVladimir Oltean 	return 0;
2675f33183bSVladimir Oltean }
2685f33183bSVladimir Oltean 
26904b67e18SVladimir Oltean /* There are 2 ways of offloading tag_8021q VLANs.
270f9bbe447SVladimir Oltean  *
27104b67e18SVladimir Oltean  * One is to use a hardware TCAM to push the port's standalone VLAN into the
27204b67e18SVladimir Oltean  * frame when forwarding it to the CPU, as an egress modification rule on the
27304b67e18SVladimir Oltean  * CPU port. This is preferable because it has no side effects for the
27404b67e18SVladimir Oltean  * autonomous forwarding path, and accomplishes tag_8021q's primary goal of
27504b67e18SVladimir Oltean  * identifying the source port of each packet based on VLAN ID.
276f9bbe447SVladimir Oltean  *
27704b67e18SVladimir Oltean  * The other is to commit the tag_8021q VLAN as a PVID to the VLAN table, and
27804b67e18SVladimir Oltean  * to configure the port as VLAN-unaware. This is less preferable because
27904b67e18SVladimir Oltean  * unique source port identification can only be done for standalone ports;
28004b67e18SVladimir Oltean  * under a VLAN-unaware bridge, all ports share the same tag_8021q VLAN as
28104b67e18SVladimir Oltean  * PVID, and under a VLAN-aware bridge, packets received by software will not
28204b67e18SVladimir Oltean  * have tag_8021q VLANs appended, just bridge VLANs.
283f9bbe447SVladimir Oltean  *
28404b67e18SVladimir Oltean  * For tag_8021q implementations of the second type, this method is used to
28504b67e18SVladimir Oltean  * replace the standalone tag_8021q VLAN of a port with the tag_8021q VLAN to
28604b67e18SVladimir Oltean  * be used for VLAN-unaware bridging.
287f9bbe447SVladimir Oltean  */
dsa_tag_8021q_bridge_join(struct dsa_switch * ds,int port,struct dsa_bridge bridge)28891495f21SVladimir Oltean int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
28991495f21SVladimir Oltean 			      struct dsa_bridge bridge)
290e19cc13cSVladimir Oltean {
29191495f21SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
29291495f21SVladimir Oltean 	u16 standalone_vid, bridge_vid;
293fac6abd5SVladimir Oltean 	int err;
294e19cc13cSVladimir Oltean 
29591495f21SVladimir Oltean 	/* Delete the standalone VLAN of the port and replace it with a
29691495f21SVladimir Oltean 	 * bridging VLAN
29791495f21SVladimir Oltean 	 */
29804b67e18SVladimir Oltean 	standalone_vid = dsa_tag_8021q_standalone_vid(dp);
299b6362bdfSVladimir Oltean 	bridge_vid = dsa_tag_8021q_bridge_vid(bridge.num);
300e19cc13cSVladimir Oltean 
30191495f21SVladimir Oltean 	err = dsa_port_tag_8021q_vlan_add(dp, bridge_vid, true);
302e19cc13cSVladimir Oltean 	if (err)
303e19cc13cSVladimir Oltean 		return err;
304e19cc13cSVladimir Oltean 
30591495f21SVladimir Oltean 	dsa_port_tag_8021q_vlan_del(dp, standalone_vid, false);
306e19cc13cSVladimir Oltean 
307e19cc13cSVladimir Oltean 	return 0;
308e19cc13cSVladimir Oltean }
30991495f21SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_join);
310e19cc13cSVladimir Oltean 
dsa_tag_8021q_bridge_leave(struct dsa_switch * ds,int port,struct dsa_bridge bridge)31191495f21SVladimir Oltean void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
312d3eed0e5SVladimir Oltean 				struct dsa_bridge bridge)
313b6ad86e6SVladimir Oltean {
31491495f21SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
31591495f21SVladimir Oltean 	u16 standalone_vid, bridge_vid;
31691495f21SVladimir Oltean 	int err;
317b6ad86e6SVladimir Oltean 
31891495f21SVladimir Oltean 	/* Delete the bridging VLAN of the port and replace it with a
31991495f21SVladimir Oltean 	 * standalone VLAN
32091495f21SVladimir Oltean 	 */
32104b67e18SVladimir Oltean 	standalone_vid = dsa_tag_8021q_standalone_vid(dp);
322b6362bdfSVladimir Oltean 	bridge_vid = dsa_tag_8021q_bridge_vid(bridge.num);
32391495f21SVladimir Oltean 
32491495f21SVladimir Oltean 	err = dsa_port_tag_8021q_vlan_add(dp, standalone_vid, false);
32591495f21SVladimir Oltean 	if (err) {
32691495f21SVladimir Oltean 		dev_err(ds->dev,
32791495f21SVladimir Oltean 			"Failed to delete tag_8021q standalone VLAN %d from port %d: %pe\n",
32891495f21SVladimir Oltean 			standalone_vid, port, ERR_PTR(err));
329b6ad86e6SVladimir Oltean 	}
330b6ad86e6SVladimir Oltean 
33191495f21SVladimir Oltean 	dsa_port_tag_8021q_vlan_del(dp, bridge_vid, true);
332b6ad86e6SVladimir Oltean }
33391495f21SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_leave);
334b6ad86e6SVladimir Oltean 
33504b67e18SVladimir Oltean /* Set up a port's standalone tag_8021q VLAN */
dsa_tag_8021q_port_setup(struct dsa_switch * ds,int port)336c64b9c05SVladimir Oltean static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port)
337f9bbe447SVladimir Oltean {
338d7b1fd52SVladimir Oltean 	struct dsa_8021q_context *ctx = ds->tag_8021q_ctx;
339c64b9c05SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
34004b67e18SVladimir Oltean 	u16 vid = dsa_tag_8021q_standalone_vid(dp);
341bbed0bbdSVladimir Oltean 	struct net_device *master;
342e19cc13cSVladimir Oltean 	int err;
343f9bbe447SVladimir Oltean 
344f9bbe447SVladimir Oltean 	/* The CPU port is implicitly configured by
345f9bbe447SVladimir Oltean 	 * configuring the front-panel ports
346f9bbe447SVladimir Oltean 	 */
347c64b9c05SVladimir Oltean 	if (!dsa_port_is_user(dp))
348f9bbe447SVladimir Oltean 		return 0;
349f9bbe447SVladimir Oltean 
3508f6a19c0SVladimir Oltean 	master = dsa_port_to_master(dp);
351bbed0bbdSVladimir Oltean 
35204b67e18SVladimir Oltean 	err = dsa_port_tag_8021q_vlan_add(dp, vid, false);
353d34d2baaSIoana Ciornei 	if (err) {
354d7b1fd52SVladimir Oltean 		dev_err(ds->dev,
35504b67e18SVladimir Oltean 			"Failed to apply standalone VID %d to port %d: %pe\n",
35604b67e18SVladimir Oltean 			vid, port, ERR_PTR(err));
357d34d2baaSIoana Ciornei 		return err;
358d34d2baaSIoana Ciornei 	}
359d34d2baaSIoana Ciornei 
36004b67e18SVladimir Oltean 	/* Add the VLAN to the master's RX filter. */
36104b67e18SVladimir Oltean 	vlan_vid_add(master, ctx->proto, vid);
362f9bbe447SVladimir Oltean 
3635f33183bSVladimir Oltean 	return err;
364f9bbe447SVladimir Oltean }
3657e092af2SVladimir Oltean 
dsa_tag_8021q_port_teardown(struct dsa_switch * ds,int port)366c64b9c05SVladimir Oltean static void dsa_tag_8021q_port_teardown(struct dsa_switch *ds, int port)
367c64b9c05SVladimir Oltean {
368c64b9c05SVladimir Oltean 	struct dsa_8021q_context *ctx = ds->tag_8021q_ctx;
369c64b9c05SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
37004b67e18SVladimir Oltean 	u16 vid = dsa_tag_8021q_standalone_vid(dp);
371c64b9c05SVladimir Oltean 	struct net_device *master;
372c64b9c05SVladimir Oltean 
373c64b9c05SVladimir Oltean 	/* The CPU port is implicitly configured by
374c64b9c05SVladimir Oltean 	 * configuring the front-panel ports
375c64b9c05SVladimir Oltean 	 */
376c64b9c05SVladimir Oltean 	if (!dsa_port_is_user(dp))
377c64b9c05SVladimir Oltean 		return;
378c64b9c05SVladimir Oltean 
3798f6a19c0SVladimir Oltean 	master = dsa_port_to_master(dp);
380c64b9c05SVladimir Oltean 
38104b67e18SVladimir Oltean 	dsa_port_tag_8021q_vlan_del(dp, vid, false);
382c64b9c05SVladimir Oltean 
38304b67e18SVladimir Oltean 	vlan_vid_del(master, ctx->proto, vid);
384c64b9c05SVladimir Oltean }
385c64b9c05SVladimir Oltean 
dsa_tag_8021q_setup(struct dsa_switch * ds)386c64b9c05SVladimir Oltean static int dsa_tag_8021q_setup(struct dsa_switch *ds)
3877e092af2SVladimir Oltean {
388a81a4574SVladimir Oltean 	int err, port;
3897e092af2SVladimir Oltean 
390bbed0bbdSVladimir Oltean 	ASSERT_RTNL();
391bbed0bbdSVladimir Oltean 
392d7b1fd52SVladimir Oltean 	for (port = 0; port < ds->num_ports; port++) {
393c64b9c05SVladimir Oltean 		err = dsa_tag_8021q_port_setup(ds, port);
394a81a4574SVladimir Oltean 		if (err < 0) {
395d7b1fd52SVladimir Oltean 			dev_err(ds->dev,
39669ebb370SVladimir Oltean 				"Failed to setup VLAN tagging for port %d: %pe\n",
39769ebb370SVladimir Oltean 				port, ERR_PTR(err));
398a81a4574SVladimir Oltean 			return err;
3997e092af2SVladimir Oltean 		}
4007e092af2SVladimir Oltean 	}
4017e092af2SVladimir Oltean 
4027e092af2SVladimir Oltean 	return 0;
4037e092af2SVladimir Oltean }
404f9bbe447SVladimir Oltean 
dsa_tag_8021q_teardown(struct dsa_switch * ds)405c64b9c05SVladimir Oltean static void dsa_tag_8021q_teardown(struct dsa_switch *ds)
406ac02a451SVladimir Oltean {
407c64b9c05SVladimir Oltean 	int port;
408ac02a451SVladimir Oltean 
409c64b9c05SVladimir Oltean 	ASSERT_RTNL();
410c64b9c05SVladimir Oltean 
411c64b9c05SVladimir Oltean 	for (port = 0; port < ds->num_ports; port++)
412c64b9c05SVladimir Oltean 		dsa_tag_8021q_port_teardown(ds, port);
413ac02a451SVladimir Oltean }
414ac02a451SVladimir Oltean 
dsa_tag_8021q_register(struct dsa_switch * ds,__be16 proto)4155da11eb4SVladimir Oltean int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto)
416cedf4670SVladimir Oltean {
417cedf4670SVladimir Oltean 	struct dsa_8021q_context *ctx;
418e0954930SVladimir Oltean 	int err;
419cedf4670SVladimir Oltean 
420cedf4670SVladimir Oltean 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
421cedf4670SVladimir Oltean 	if (!ctx)
422d7b1fd52SVladimir Oltean 		return -ENOMEM;
423cedf4670SVladimir Oltean 
424cedf4670SVladimir Oltean 	ctx->proto = proto;
425cedf4670SVladimir Oltean 	ctx->ds = ds;
426cedf4670SVladimir Oltean 
427c64b9c05SVladimir Oltean 	INIT_LIST_HEAD(&ctx->vlans);
428cedf4670SVladimir Oltean 
429d7b1fd52SVladimir Oltean 	ds->tag_8021q_ctx = ctx;
430d7b1fd52SVladimir Oltean 
431e0954930SVladimir Oltean 	err = dsa_tag_8021q_setup(ds);
432e0954930SVladimir Oltean 	if (err)
433e0954930SVladimir Oltean 		goto err_free;
434e0954930SVladimir Oltean 
435e0954930SVladimir Oltean 	return 0;
436e0954930SVladimir Oltean 
437e0954930SVladimir Oltean err_free:
438e0954930SVladimir Oltean 	kfree(ctx);
439e0954930SVladimir Oltean 	return err;
440cedf4670SVladimir Oltean }
441cedf4670SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_register);
442cedf4670SVladimir Oltean 
dsa_tag_8021q_unregister(struct dsa_switch * ds)443d7b1fd52SVladimir Oltean void dsa_tag_8021q_unregister(struct dsa_switch *ds)
444cedf4670SVladimir Oltean {
445d7b1fd52SVladimir Oltean 	struct dsa_8021q_context *ctx = ds->tag_8021q_ctx;
446c64b9c05SVladimir Oltean 	struct dsa_tag_8021q_vlan *v, *n;
447328621f6SVladimir Oltean 
448c64b9c05SVladimir Oltean 	dsa_tag_8021q_teardown(ds);
449cedf4670SVladimir Oltean 
450c64b9c05SVladimir Oltean 	list_for_each_entry_safe(v, n, &ctx->vlans, list) {
451c64b9c05SVladimir Oltean 		list_del(&v->list);
452c64b9c05SVladimir Oltean 		kfree(v);
453cedf4670SVladimir Oltean 	}
454cedf4670SVladimir Oltean 
455d7b1fd52SVladimir Oltean 	ds->tag_8021q_ctx = NULL;
456d7b1fd52SVladimir Oltean 
457cedf4670SVladimir Oltean 	kfree(ctx);
458cedf4670SVladimir Oltean }
459cedf4670SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_unregister);
460cedf4670SVladimir Oltean 
dsa_8021q_xmit(struct sk_buff * skb,struct net_device * netdev,u16 tpid,u16 tci)461f9bbe447SVladimir Oltean struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
462f9bbe447SVladimir Oltean 			       u16 tpid, u16 tci)
463f9bbe447SVladimir Oltean {
464*f0a9d563SVladimir Oltean 	/* skb->data points at the MAC header, which is fine
465*f0a9d563SVladimir Oltean 	 * for vlan_insert_tag().
466f9bbe447SVladimir Oltean 	 */
467f9bbe447SVladimir Oltean 	return vlan_insert_tag(skb, htons(tpid), tci);
468f9bbe447SVladimir Oltean }
469f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_xmit);
470f9bbe447SVladimir Oltean 
dsa_tag_8021q_find_port_by_vbid(struct net_device * master,int vbid)471d7f9787aSVladimir Oltean struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *master,
472d7f9787aSVladimir Oltean 						   int vbid)
473d7f9787aSVladimir Oltean {
474d7f9787aSVladimir Oltean 	struct dsa_port *cpu_dp = master->dsa_ptr;
475d7f9787aSVladimir Oltean 	struct dsa_switch_tree *dst = cpu_dp->dst;
476d7f9787aSVladimir Oltean 	struct dsa_port *dp;
477d7f9787aSVladimir Oltean 
478d7f9787aSVladimir Oltean 	if (WARN_ON(!vbid))
479d7f9787aSVladimir Oltean 		return NULL;
480d7f9787aSVladimir Oltean 
481d7f9787aSVladimir Oltean 	dsa_tree_for_each_user_port(dp, dst) {
482d7f9787aSVladimir Oltean 		if (!dp->bridge)
483d7f9787aSVladimir Oltean 			continue;
484d7f9787aSVladimir Oltean 
485d7f9787aSVladimir Oltean 		if (dp->stp_state != BR_STATE_LEARNING &&
486d7f9787aSVladimir Oltean 		    dp->stp_state != BR_STATE_FORWARDING)
487d7f9787aSVladimir Oltean 			continue;
488d7f9787aSVladimir Oltean 
489d7f9787aSVladimir Oltean 		if (dp->cpu_dp != cpu_dp)
490d7f9787aSVladimir Oltean 			continue;
491d7f9787aSVladimir Oltean 
492d7f9787aSVladimir Oltean 		if (dsa_port_bridge_num_get(dp) == vbid)
493d7f9787aSVladimir Oltean 			return dp->slave;
494d7f9787aSVladimir Oltean 	}
495d7f9787aSVladimir Oltean 
496d7f9787aSVladimir Oltean 	return NULL;
497d7f9787aSVladimir Oltean }
498d7f9787aSVladimir Oltean EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_port_by_vbid);
499d7f9787aSVladimir Oltean 
dsa_8021q_rcv(struct sk_buff * skb,int * source_port,int * switch_id,int * vbid)500d7f9787aSVladimir Oltean void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id,
501d7f9787aSVladimir Oltean 		   int *vbid)
502233697b3SVladimir Oltean {
503233697b3SVladimir Oltean 	u16 vid, tci;
504233697b3SVladimir Oltean 
505233697b3SVladimir Oltean 	if (skb_vlan_tag_present(skb)) {
506233697b3SVladimir Oltean 		tci = skb_vlan_tag_get(skb);
507233697b3SVladimir Oltean 		__vlan_hwaccel_clear_tag(skb);
508233697b3SVladimir Oltean 	} else {
509c8620335SVladimir Oltean 		skb_push_rcsum(skb, ETH_HLEN);
510233697b3SVladimir Oltean 		__skb_vlan_pop(skb, &tci);
511233697b3SVladimir Oltean 		skb_pull_rcsum(skb, ETH_HLEN);
512c8620335SVladimir Oltean 	}
513233697b3SVladimir Oltean 
514233697b3SVladimir Oltean 	vid = tci & VLAN_VID_MASK;
515233697b3SVladimir Oltean 
516233697b3SVladimir Oltean 	*source_port = dsa_8021q_rx_source_port(vid);
517233697b3SVladimir Oltean 	*switch_id = dsa_8021q_rx_switch_id(vid);
518d7f9787aSVladimir Oltean 
519d7f9787aSVladimir Oltean 	if (vbid)
520d7f9787aSVladimir Oltean 		*vbid = dsa_tag_8021q_rx_vbid(vid);
521d7f9787aSVladimir Oltean 
522233697b3SVladimir Oltean 	skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
523233697b3SVladimir Oltean }
524233697b3SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rcv);
525