xref: /linux/net/dsa/switch.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f515f192SVivien Didelot /*
3f515f192SVivien Didelot  * Handling of a single switch chip, part of a switch fabric
4f515f192SVivien Didelot  *
54333d619SVivien Didelot  * Copyright (c) 2017 Savoir-faire Linux Inc.
64333d619SVivien Didelot  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7f515f192SVivien Didelot  */
8f515f192SVivien Didelot 
9d371b7c9SVladimir Oltean #include <linux/if_bridge.h>
10f515f192SVivien Didelot #include <linux/netdevice.h>
11f515f192SVivien Didelot #include <linux/notifier.h>
12061f6a50SFlorian Fainelli #include <linux/if_vlan.h>
131faabf74SVivien Didelot #include <net/switchdev.h>
14ea5dd34bSVivien Didelot 
1547d2ce03SVladimir Oltean #include "dsa.h"
165917bfe6SVladimir Oltean #include "netlink.h"
17022bba63SVladimir Oltean #include "port.h"
180c603136SVladimir Oltean #include "switch.h"
1919d05ea7SVladimir Oltean #include "tag_8021q.h"
209538ebceSVladimir Oltean #include "trace.h"
21*6ca80638SFlorian Fainelli #include "user.h"
22f515f192SVivien Didelot 
dsa_switch_fastest_ageing_time(struct dsa_switch * ds,unsigned int ageing_time)231faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
241faabf74SVivien Didelot 						   unsigned int ageing_time)
251faabf74SVivien Didelot {
26d0004a02SVladimir Oltean 	struct dsa_port *dp;
271faabf74SVivien Didelot 
28d0004a02SVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
291faabf74SVivien Didelot 		if (dp->ageing_time && dp->ageing_time < ageing_time)
301faabf74SVivien Didelot 			ageing_time = dp->ageing_time;
311faabf74SVivien Didelot 
321faabf74SVivien Didelot 	return ageing_time;
331faabf74SVivien Didelot }
341faabf74SVivien Didelot 
dsa_switch_ageing_time(struct dsa_switch * ds,struct dsa_notifier_ageing_time_info * info)351faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds,
361faabf74SVivien Didelot 				  struct dsa_notifier_ageing_time_info *info)
371faabf74SVivien Didelot {
381faabf74SVivien Didelot 	unsigned int ageing_time = info->ageing_time;
391faabf74SVivien Didelot 
401faabf74SVivien Didelot 	if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
411faabf74SVivien Didelot 		return -ERANGE;
4277b61365SVladimir Oltean 
431faabf74SVivien Didelot 	if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
441faabf74SVivien Didelot 		return -ERANGE;
451faabf74SVivien Didelot 
461faabf74SVivien Didelot 	/* Program the fastest ageing time in case of multiple bridges */
471faabf74SVivien Didelot 	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
481faabf74SVivien Didelot 
491faabf74SVivien Didelot 	if (ds->ops->set_ageing_time)
501faabf74SVivien Didelot 		return ds->ops->set_ageing_time(ds, ageing_time);
511faabf74SVivien Didelot 
521faabf74SVivien Didelot 	return 0;
531faabf74SVivien Didelot }
541faabf74SVivien Didelot 
dsa_port_mtu_match(struct dsa_port * dp,struct dsa_notifier_mtu_info * info)55fac6abd5SVladimir Oltean static bool dsa_port_mtu_match(struct dsa_port *dp,
56bfcb8132SVladimir Oltean 			       struct dsa_notifier_mtu_info *info)
57bfcb8132SVladimir Oltean {
58be6ff966SVladimir Oltean 	return dp == info->dp || dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp);
59bfcb8132SVladimir Oltean }
60bfcb8132SVladimir Oltean 
dsa_switch_mtu(struct dsa_switch * ds,struct dsa_notifier_mtu_info * info)61bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds,
62bfcb8132SVladimir Oltean 			  struct dsa_notifier_mtu_info *info)
63bfcb8132SVladimir Oltean {
64fac6abd5SVladimir Oltean 	struct dsa_port *dp;
65fac6abd5SVladimir Oltean 	int ret;
66bfcb8132SVladimir Oltean 
67bfcb8132SVladimir Oltean 	if (!ds->ops->port_change_mtu)
68bfcb8132SVladimir Oltean 		return -EOPNOTSUPP;
69bfcb8132SVladimir Oltean 
70fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
71fac6abd5SVladimir Oltean 		if (dsa_port_mtu_match(dp, info)) {
72fac6abd5SVladimir Oltean 			ret = ds->ops->port_change_mtu(ds, dp->index,
73fac6abd5SVladimir Oltean 						       info->mtu);
74bfcb8132SVladimir Oltean 			if (ret)
75bfcb8132SVladimir Oltean 				return ret;
76bfcb8132SVladimir Oltean 		}
77bfcb8132SVladimir Oltean 	}
78bfcb8132SVladimir Oltean 
79bfcb8132SVladimir Oltean 	return 0;
80bfcb8132SVladimir Oltean }
81bfcb8132SVladimir Oltean 
dsa_switch_bridge_join(struct dsa_switch * ds,struct dsa_notifier_bridge_info * info)8204d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds,
8304d3a4c6SVivien Didelot 				  struct dsa_notifier_bridge_info *info)
8404d3a4c6SVivien Didelot {
85e19cc13cSVladimir Oltean 	int err;
86f66a6a69SVladimir Oltean 
87726816a1SVladimir Oltean 	if (info->dp->ds == ds) {
8867b5fb5dSVladimir Oltean 		if (!ds->ops->port_bridge_join)
8967b5fb5dSVladimir Oltean 			return -EOPNOTSUPP;
9067b5fb5dSVladimir Oltean 
91726816a1SVladimir Oltean 		err = ds->ops->port_bridge_join(ds, info->dp->index,
92726816a1SVladimir Oltean 						info->bridge,
9306b9cce4SVladimir Oltean 						&info->tx_fwd_offload,
9406b9cce4SVladimir Oltean 						info->extack);
95e19cc13cSVladimir Oltean 		if (err)
96e19cc13cSVladimir Oltean 			return err;
97e19cc13cSVladimir Oltean 	}
9804d3a4c6SVivien Didelot 
99726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_bridge_join) {
100726816a1SVladimir Oltean 		err = ds->ops->crosschip_bridge_join(ds,
101726816a1SVladimir Oltean 						     info->dp->ds->dst->index,
102726816a1SVladimir Oltean 						     info->dp->ds->index,
103726816a1SVladimir Oltean 						     info->dp->index,
104726816a1SVladimir Oltean 						     info->bridge,
10506b9cce4SVladimir Oltean 						     info->extack);
106e19cc13cSVladimir Oltean 		if (err)
107e19cc13cSVladimir Oltean 			return err;
108e19cc13cSVladimir Oltean 	}
10904d3a4c6SVivien Didelot 
11091495f21SVladimir Oltean 	return 0;
11104d3a4c6SVivien Didelot }
11204d3a4c6SVivien Didelot 
dsa_switch_bridge_leave(struct dsa_switch * ds,struct dsa_notifier_bridge_info * info)113381a7301STobias Waldekranz static int dsa_switch_bridge_leave(struct dsa_switch *ds,
114381a7301STobias Waldekranz 				   struct dsa_notifier_bridge_info *info)
115381a7301STobias Waldekranz {
116726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_bridge_leave)
117726816a1SVladimir Oltean 		ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge);
118381a7301STobias Waldekranz 
119726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_bridge_leave)
120726816a1SVladimir Oltean 		ds->ops->crosschip_bridge_leave(ds, info->dp->ds->dst->index,
121726816a1SVladimir Oltean 						info->dp->ds->index,
122726816a1SVladimir Oltean 						info->dp->index,
123381a7301STobias Waldekranz 						info->bridge);
124381a7301STobias Waldekranz 
12591495f21SVladimir Oltean 	return 0;
12604d3a4c6SVivien Didelot }
12704d3a4c6SVivien Didelot 
128b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing
129b8e997c4SVladimir Oltean  * DSA links) that sit between the targeted port on which the notifier was
130b8e997c4SVladimir Oltean  * emitted and its dedicated CPU port.
131b8e997c4SVladimir Oltean  */
dsa_port_host_address_match(struct dsa_port * dp,const struct dsa_port * targeted_dp)132fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp,
133726816a1SVladimir Oltean 					const struct dsa_port *targeted_dp)
134b8e997c4SVladimir Oltean {
135726816a1SVladimir Oltean 	struct dsa_port *cpu_dp = targeted_dp->cpu_dp;
136b8e997c4SVladimir Oltean 
137726816a1SVladimir Oltean 	if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds))
138fac6abd5SVladimir Oltean 		return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index,
139b8e997c4SVladimir Oltean 						     cpu_dp->index);
140b8e997c4SVladimir Oltean 
141b8e997c4SVladimir Oltean 	return false;
142b8e997c4SVladimir Oltean }
143b8e997c4SVladimir Oltean 
dsa_mac_addr_find(struct list_head * addr_list,const unsigned char * addr,u16 vid,struct dsa_db db)144161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list,
145c2693363SVladimir Oltean 					      const unsigned char *addr, u16 vid,
146c2693363SVladimir Oltean 					      struct dsa_db db)
147161ca59dSVladimir Oltean {
148161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
149161ca59dSVladimir Oltean 
150161ca59dSVladimir Oltean 	list_for_each_entry(a, addr_list, list)
151c2693363SVladimir Oltean 		if (ether_addr_equal(a->addr, addr) && a->vid == vid &&
152c2693363SVladimir Oltean 		    dsa_db_equal(&a->db, &db))
153161ca59dSVladimir Oltean 			return a;
154161ca59dSVladimir Oltean 
155161ca59dSVladimir Oltean 	return NULL;
156161ca59dSVladimir Oltean }
157161ca59dSVladimir Oltean 
dsa_port_do_mdb_add(struct dsa_port * dp,const struct switchdev_obj_port_mdb * mdb,struct dsa_db db)158fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp,
159c2693363SVladimir Oltean 			       const struct switchdev_obj_port_mdb *mdb,
160c2693363SVladimir Oltean 			       struct dsa_db db)
161161ca59dSVladimir Oltean {
162fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
163161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
164fac6abd5SVladimir Oltean 	int port = dp->index;
165338a3a47SVladimir Oltean 	int err = 0;
166161ca59dSVladimir Oltean 
167161ca59dSVladimir Oltean 	/* No need to bother with refcounting for user ports */
1689538ebceSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
1699538ebceSVladimir Oltean 		err = ds->ops->port_mdb_add(ds, port, mdb, db);
1709538ebceSVladimir Oltean 		trace_dsa_mdb_add_hw(dp, mdb->addr, mdb->vid, &db, err);
1719538ebceSVladimir Oltean 
1729538ebceSVladimir Oltean 		return err;
1739538ebceSVladimir Oltean 	}
174161ca59dSVladimir Oltean 
175338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
176338a3a47SVladimir Oltean 
177c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);
178161ca59dSVladimir Oltean 	if (a) {
179161ca59dSVladimir Oltean 		refcount_inc(&a->refcount);
1809538ebceSVladimir Oltean 		trace_dsa_mdb_add_bump(dp, mdb->addr, mdb->vid, &db,
1819538ebceSVladimir Oltean 				       &a->refcount);
182338a3a47SVladimir Oltean 		goto out;
183161ca59dSVladimir Oltean 	}
184161ca59dSVladimir Oltean 
185161ca59dSVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
186338a3a47SVladimir Oltean 	if (!a) {
187338a3a47SVladimir Oltean 		err = -ENOMEM;
188338a3a47SVladimir Oltean 		goto out;
189338a3a47SVladimir Oltean 	}
190161ca59dSVladimir Oltean 
191c2693363SVladimir Oltean 	err = ds->ops->port_mdb_add(ds, port, mdb, db);
1929538ebceSVladimir Oltean 	trace_dsa_mdb_add_hw(dp, mdb->addr, mdb->vid, &db, err);
193161ca59dSVladimir Oltean 	if (err) {
194161ca59dSVladimir Oltean 		kfree(a);
195338a3a47SVladimir Oltean 		goto out;
196161ca59dSVladimir Oltean 	}
197161ca59dSVladimir Oltean 
198161ca59dSVladimir Oltean 	ether_addr_copy(a->addr, mdb->addr);
199161ca59dSVladimir Oltean 	a->vid = mdb->vid;
200c2693363SVladimir Oltean 	a->db = db;
201161ca59dSVladimir Oltean 	refcount_set(&a->refcount, 1);
202161ca59dSVladimir Oltean 	list_add_tail(&a->list, &dp->mdbs);
203161ca59dSVladimir Oltean 
204338a3a47SVladimir Oltean out:
205338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
206338a3a47SVladimir Oltean 
207338a3a47SVladimir Oltean 	return err;
208161ca59dSVladimir Oltean }
209161ca59dSVladimir Oltean 
dsa_port_do_mdb_del(struct dsa_port * dp,const struct switchdev_obj_port_mdb * mdb,struct dsa_db db)210fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp,
211c2693363SVladimir Oltean 			       const struct switchdev_obj_port_mdb *mdb,
212c2693363SVladimir Oltean 			       struct dsa_db db)
213161ca59dSVladimir Oltean {
214fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
215161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
216fac6abd5SVladimir Oltean 	int port = dp->index;
217338a3a47SVladimir Oltean 	int err = 0;
218161ca59dSVladimir Oltean 
219161ca59dSVladimir Oltean 	/* No need to bother with refcounting for user ports */
2209538ebceSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
2219538ebceSVladimir Oltean 		err = ds->ops->port_mdb_del(ds, port, mdb, db);
2229538ebceSVladimir Oltean 		trace_dsa_mdb_del_hw(dp, mdb->addr, mdb->vid, &db, err);
2239538ebceSVladimir Oltean 
2249538ebceSVladimir Oltean 		return err;
2259538ebceSVladimir Oltean 	}
226161ca59dSVladimir Oltean 
227338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
228338a3a47SVladimir Oltean 
229c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);
230338a3a47SVladimir Oltean 	if (!a) {
2319538ebceSVladimir Oltean 		trace_dsa_mdb_del_not_found(dp, mdb->addr, mdb->vid, &db);
232338a3a47SVladimir Oltean 		err = -ENOENT;
233338a3a47SVladimir Oltean 		goto out;
234338a3a47SVladimir Oltean 	}
235161ca59dSVladimir Oltean 
2369538ebceSVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount)) {
2379538ebceSVladimir Oltean 		trace_dsa_mdb_del_drop(dp, mdb->addr, mdb->vid, &db,
2389538ebceSVladimir Oltean 				       &a->refcount);
239338a3a47SVladimir Oltean 		goto out;
2409538ebceSVladimir Oltean 	}
241161ca59dSVladimir Oltean 
242c2693363SVladimir Oltean 	err = ds->ops->port_mdb_del(ds, port, mdb, db);
2439538ebceSVladimir Oltean 	trace_dsa_mdb_del_hw(dp, mdb->addr, mdb->vid, &db, err);
244161ca59dSVladimir Oltean 	if (err) {
245232deb3fSVladimir Oltean 		refcount_set(&a->refcount, 1);
246338a3a47SVladimir Oltean 		goto out;
247161ca59dSVladimir Oltean 	}
248161ca59dSVladimir Oltean 
249161ca59dSVladimir Oltean 	list_del(&a->list);
250161ca59dSVladimir Oltean 	kfree(a);
251161ca59dSVladimir Oltean 
252338a3a47SVladimir Oltean out:
253338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
254338a3a47SVladimir Oltean 
255338a3a47SVladimir Oltean 	return err;
256161ca59dSVladimir Oltean }
257161ca59dSVladimir Oltean 
dsa_port_do_fdb_add(struct dsa_port * dp,const unsigned char * addr,u16 vid,struct dsa_db db)258fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
259c2693363SVladimir Oltean 			       u16 vid, struct dsa_db db)
2603f6e32f9SVladimir Oltean {
261fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
2623f6e32f9SVladimir Oltean 	struct dsa_mac_addr *a;
263fac6abd5SVladimir Oltean 	int port = dp->index;
264338a3a47SVladimir Oltean 	int err = 0;
2653f6e32f9SVladimir Oltean 
2663f6e32f9SVladimir Oltean 	/* No need to bother with refcounting for user ports */
2679538ebceSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
2689538ebceSVladimir Oltean 		err = ds->ops->port_fdb_add(ds, port, addr, vid, db);
2699538ebceSVladimir Oltean 		trace_dsa_fdb_add_hw(dp, addr, vid, &db, err);
2709538ebceSVladimir Oltean 
2719538ebceSVladimir Oltean 		return err;
2729538ebceSVladimir Oltean 	}
2733f6e32f9SVladimir Oltean 
274338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
275338a3a47SVladimir Oltean 
276c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);
2773f6e32f9SVladimir Oltean 	if (a) {
2783f6e32f9SVladimir Oltean 		refcount_inc(&a->refcount);
2799538ebceSVladimir Oltean 		trace_dsa_fdb_add_bump(dp, addr, vid, &db, &a->refcount);
280338a3a47SVladimir Oltean 		goto out;
2813f6e32f9SVladimir Oltean 	}
2823f6e32f9SVladimir Oltean 
2833f6e32f9SVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
284338a3a47SVladimir Oltean 	if (!a) {
285338a3a47SVladimir Oltean 		err = -ENOMEM;
286338a3a47SVladimir Oltean 		goto out;
287338a3a47SVladimir Oltean 	}
2883f6e32f9SVladimir Oltean 
289c2693363SVladimir Oltean 	err = ds->ops->port_fdb_add(ds, port, addr, vid, db);
2909538ebceSVladimir Oltean 	trace_dsa_fdb_add_hw(dp, addr, vid, &db, err);
2913f6e32f9SVladimir Oltean 	if (err) {
2923f6e32f9SVladimir Oltean 		kfree(a);
293338a3a47SVladimir Oltean 		goto out;
2943f6e32f9SVladimir Oltean 	}
2953f6e32f9SVladimir Oltean 
2963f6e32f9SVladimir Oltean 	ether_addr_copy(a->addr, addr);
2973f6e32f9SVladimir Oltean 	a->vid = vid;
298c2693363SVladimir Oltean 	a->db = db;
2993f6e32f9SVladimir Oltean 	refcount_set(&a->refcount, 1);
3003f6e32f9SVladimir Oltean 	list_add_tail(&a->list, &dp->fdbs);
3013f6e32f9SVladimir Oltean 
302338a3a47SVladimir Oltean out:
303338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
304338a3a47SVladimir Oltean 
305338a3a47SVladimir Oltean 	return err;
3063f6e32f9SVladimir Oltean }
3073f6e32f9SVladimir Oltean 
dsa_port_do_fdb_del(struct dsa_port * dp,const unsigned char * addr,u16 vid,struct dsa_db db)308fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
309c2693363SVladimir Oltean 			       u16 vid, struct dsa_db db)
3103f6e32f9SVladimir Oltean {
311fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
3123f6e32f9SVladimir Oltean 	struct dsa_mac_addr *a;
313fac6abd5SVladimir Oltean 	int port = dp->index;
314338a3a47SVladimir Oltean 	int err = 0;
3153f6e32f9SVladimir Oltean 
3163f6e32f9SVladimir Oltean 	/* No need to bother with refcounting for user ports */
3179538ebceSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
3189538ebceSVladimir Oltean 		err = ds->ops->port_fdb_del(ds, port, addr, vid, db);
3199538ebceSVladimir Oltean 		trace_dsa_fdb_del_hw(dp, addr, vid, &db, err);
3209538ebceSVladimir Oltean 
3219538ebceSVladimir Oltean 		return err;
3229538ebceSVladimir Oltean 	}
3233f6e32f9SVladimir Oltean 
324338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
325338a3a47SVladimir Oltean 
326c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);
327338a3a47SVladimir Oltean 	if (!a) {
3289538ebceSVladimir Oltean 		trace_dsa_fdb_del_not_found(dp, addr, vid, &db);
329338a3a47SVladimir Oltean 		err = -ENOENT;
330338a3a47SVladimir Oltean 		goto out;
331338a3a47SVladimir Oltean 	}
3323f6e32f9SVladimir Oltean 
3339538ebceSVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount)) {
3349538ebceSVladimir Oltean 		trace_dsa_fdb_del_drop(dp, addr, vid, &db, &a->refcount);
335338a3a47SVladimir Oltean 		goto out;
3369538ebceSVladimir Oltean 	}
3373f6e32f9SVladimir Oltean 
338c2693363SVladimir Oltean 	err = ds->ops->port_fdb_del(ds, port, addr, vid, db);
3399538ebceSVladimir Oltean 	trace_dsa_fdb_del_hw(dp, addr, vid, &db, err);
3403f6e32f9SVladimir Oltean 	if (err) {
341232deb3fSVladimir Oltean 		refcount_set(&a->refcount, 1);
342338a3a47SVladimir Oltean 		goto out;
3433f6e32f9SVladimir Oltean 	}
3443f6e32f9SVladimir Oltean 
3453f6e32f9SVladimir Oltean 	list_del(&a->list);
3463f6e32f9SVladimir Oltean 	kfree(a);
3473f6e32f9SVladimir Oltean 
348338a3a47SVladimir Oltean out:
349338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
350338a3a47SVladimir Oltean 
351338a3a47SVladimir Oltean 	return err;
3523f6e32f9SVladimir Oltean }
3533f6e32f9SVladimir Oltean 
dsa_switch_do_lag_fdb_add(struct dsa_switch * ds,struct dsa_lag * lag,const unsigned char * addr,u16 vid,struct dsa_db db)354e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag,
355c2693363SVladimir Oltean 				     const unsigned char *addr, u16 vid,
356c2693363SVladimir Oltean 				     struct dsa_db db)
357e212fa7cSVladimir Oltean {
358e212fa7cSVladimir Oltean 	struct dsa_mac_addr *a;
359e212fa7cSVladimir Oltean 	int err = 0;
360e212fa7cSVladimir Oltean 
361e212fa7cSVladimir Oltean 	mutex_lock(&lag->fdb_lock);
362e212fa7cSVladimir Oltean 
363c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);
364e212fa7cSVladimir Oltean 	if (a) {
365e212fa7cSVladimir Oltean 		refcount_inc(&a->refcount);
3669538ebceSVladimir Oltean 		trace_dsa_lag_fdb_add_bump(lag->dev, addr, vid, &db,
3679538ebceSVladimir Oltean 					   &a->refcount);
368e212fa7cSVladimir Oltean 		goto out;
369e212fa7cSVladimir Oltean 	}
370e212fa7cSVladimir Oltean 
371e212fa7cSVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
372e212fa7cSVladimir Oltean 	if (!a) {
373e212fa7cSVladimir Oltean 		err = -ENOMEM;
374e212fa7cSVladimir Oltean 		goto out;
375e212fa7cSVladimir Oltean 	}
376e212fa7cSVladimir Oltean 
377c2693363SVladimir Oltean 	err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db);
3789538ebceSVladimir Oltean 	trace_dsa_lag_fdb_add_hw(lag->dev, addr, vid, &db, err);
379e212fa7cSVladimir Oltean 	if (err) {
380e212fa7cSVladimir Oltean 		kfree(a);
381e212fa7cSVladimir Oltean 		goto out;
382e212fa7cSVladimir Oltean 	}
383e212fa7cSVladimir Oltean 
384e212fa7cSVladimir Oltean 	ether_addr_copy(a->addr, addr);
385e212fa7cSVladimir Oltean 	a->vid = vid;
386c7560d12SVladimir Oltean 	a->db = db;
387e212fa7cSVladimir Oltean 	refcount_set(&a->refcount, 1);
388e212fa7cSVladimir Oltean 	list_add_tail(&a->list, &lag->fdbs);
389e212fa7cSVladimir Oltean 
390e212fa7cSVladimir Oltean out:
391e212fa7cSVladimir Oltean 	mutex_unlock(&lag->fdb_lock);
392e212fa7cSVladimir Oltean 
393e212fa7cSVladimir Oltean 	return err;
394e212fa7cSVladimir Oltean }
395e212fa7cSVladimir Oltean 
dsa_switch_do_lag_fdb_del(struct dsa_switch * ds,struct dsa_lag * lag,const unsigned char * addr,u16 vid,struct dsa_db db)396e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag,
397c2693363SVladimir Oltean 				     const unsigned char *addr, u16 vid,
398c2693363SVladimir Oltean 				     struct dsa_db db)
399e212fa7cSVladimir Oltean {
400e212fa7cSVladimir Oltean 	struct dsa_mac_addr *a;
401e212fa7cSVladimir Oltean 	int err = 0;
402e212fa7cSVladimir Oltean 
403e212fa7cSVladimir Oltean 	mutex_lock(&lag->fdb_lock);
404e212fa7cSVladimir Oltean 
405c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);
406e212fa7cSVladimir Oltean 	if (!a) {
4079538ebceSVladimir Oltean 		trace_dsa_lag_fdb_del_not_found(lag->dev, addr, vid, &db);
408e212fa7cSVladimir Oltean 		err = -ENOENT;
409e212fa7cSVladimir Oltean 		goto out;
410e212fa7cSVladimir Oltean 	}
411e212fa7cSVladimir Oltean 
4129538ebceSVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount)) {
4139538ebceSVladimir Oltean 		trace_dsa_lag_fdb_del_drop(lag->dev, addr, vid, &db,
4149538ebceSVladimir Oltean 					   &a->refcount);
415e212fa7cSVladimir Oltean 		goto out;
4169538ebceSVladimir Oltean 	}
417e212fa7cSVladimir Oltean 
418c2693363SVladimir Oltean 	err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db);
4199538ebceSVladimir Oltean 	trace_dsa_lag_fdb_del_hw(lag->dev, addr, vid, &db, err);
420e212fa7cSVladimir Oltean 	if (err) {
421e212fa7cSVladimir Oltean 		refcount_set(&a->refcount, 1);
422e212fa7cSVladimir Oltean 		goto out;
423e212fa7cSVladimir Oltean 	}
424e212fa7cSVladimir Oltean 
425e212fa7cSVladimir Oltean 	list_del(&a->list);
426e212fa7cSVladimir Oltean 	kfree(a);
427e212fa7cSVladimir Oltean 
428e212fa7cSVladimir Oltean out:
429e212fa7cSVladimir Oltean 	mutex_unlock(&lag->fdb_lock);
430e212fa7cSVladimir Oltean 
431e212fa7cSVladimir Oltean 	return err;
432e212fa7cSVladimir Oltean }
433e212fa7cSVladimir Oltean 
dsa_switch_host_fdb_add(struct dsa_switch * ds,struct dsa_notifier_fdb_info * info)4343dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
4353dc80afcSVladimir Oltean 				   struct dsa_notifier_fdb_info *info)
4363dc80afcSVladimir Oltean {
437fac6abd5SVladimir Oltean 	struct dsa_port *dp;
4383dc80afcSVladimir Oltean 	int err = 0;
4393dc80afcSVladimir Oltean 
4403dc80afcSVladimir Oltean 	if (!ds->ops->port_fdb_add)
4413dc80afcSVladimir Oltean 		return -EOPNOTSUPP;
4423dc80afcSVladimir Oltean 
443fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
444726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
445acc43b7bSVladimir Oltean 			if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) {
446acc43b7bSVladimir Oltean 				err = dsa_switch_do_lag_fdb_add(ds, dp->lag,
447acc43b7bSVladimir Oltean 								info->addr,
448acc43b7bSVladimir Oltean 								info->vid,
449c2693363SVladimir Oltean 								info->db);
450acc43b7bSVladimir Oltean 			} else {
451acc43b7bSVladimir Oltean 				err = dsa_port_do_fdb_add(dp, info->addr,
452acc43b7bSVladimir Oltean 							  info->vid, info->db);
453acc43b7bSVladimir Oltean 			}
4543dc80afcSVladimir Oltean 			if (err)
4553dc80afcSVladimir Oltean 				break;
4563dc80afcSVladimir Oltean 		}
4573dc80afcSVladimir Oltean 	}
4583dc80afcSVladimir Oltean 
4593dc80afcSVladimir Oltean 	return err;
4603dc80afcSVladimir Oltean }
4613dc80afcSVladimir Oltean 
dsa_switch_host_fdb_del(struct dsa_switch * ds,struct dsa_notifier_fdb_info * info)4623dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
4633dc80afcSVladimir Oltean 				   struct dsa_notifier_fdb_info *info)
4643dc80afcSVladimir Oltean {
465fac6abd5SVladimir Oltean 	struct dsa_port *dp;
4663f6e32f9SVladimir Oltean 	int err = 0;
4673f6e32f9SVladimir Oltean 
4683dc80afcSVladimir Oltean 	if (!ds->ops->port_fdb_del)
4693dc80afcSVladimir Oltean 		return -EOPNOTSUPP;
4703dc80afcSVladimir Oltean 
471fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
472726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
473acc43b7bSVladimir Oltean 			if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) {
474acc43b7bSVladimir Oltean 				err = dsa_switch_do_lag_fdb_del(ds, dp->lag,
475acc43b7bSVladimir Oltean 								info->addr,
476acc43b7bSVladimir Oltean 								info->vid,
477c2693363SVladimir Oltean 								info->db);
478acc43b7bSVladimir Oltean 			} else {
479acc43b7bSVladimir Oltean 				err = dsa_port_do_fdb_del(dp, info->addr,
480acc43b7bSVladimir Oltean 							  info->vid, info->db);
481acc43b7bSVladimir Oltean 			}
4823f6e32f9SVladimir Oltean 			if (err)
4833f6e32f9SVladimir Oltean 				break;
4843f6e32f9SVladimir Oltean 		}
4853f6e32f9SVladimir Oltean 	}
4863dc80afcSVladimir Oltean 
4873f6e32f9SVladimir Oltean 	return err;
4883dc80afcSVladimir Oltean }
4893dc80afcSVladimir Oltean 
dsa_switch_fdb_add(struct dsa_switch * ds,struct dsa_notifier_fdb_info * info)490685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds,
491685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
492685fb6a4SVivien Didelot {
493726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
494fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
495685fb6a4SVivien Didelot 
4961b6dd556SArkadi Sharshevsky 	if (!ds->ops->port_fdb_add)
497685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
498685fb6a4SVivien Didelot 
499c2693363SVladimir Oltean 	return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db);
500685fb6a4SVivien Didelot }
501685fb6a4SVivien Didelot 
dsa_switch_fdb_del(struct dsa_switch * ds,struct dsa_notifier_fdb_info * info)502685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds,
503685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
504685fb6a4SVivien Didelot {
505726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
506fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
507685fb6a4SVivien Didelot 
508685fb6a4SVivien Didelot 	if (!ds->ops->port_fdb_del)
509685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
510685fb6a4SVivien Didelot 
511c2693363SVladimir Oltean 	return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db);
512685fb6a4SVivien Didelot }
513685fb6a4SVivien Didelot 
dsa_switch_lag_fdb_add(struct dsa_switch * ds,struct dsa_notifier_lag_fdb_info * info)514e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_add(struct dsa_switch *ds,
515e212fa7cSVladimir Oltean 				  struct dsa_notifier_lag_fdb_info *info)
516e212fa7cSVladimir Oltean {
517e212fa7cSVladimir Oltean 	struct dsa_port *dp;
518e212fa7cSVladimir Oltean 
519e212fa7cSVladimir Oltean 	if (!ds->ops->lag_fdb_add)
520e212fa7cSVladimir Oltean 		return -EOPNOTSUPP;
521e212fa7cSVladimir Oltean 
522e212fa7cSVladimir Oltean 	/* Notify switch only if it has a port in this LAG */
523e212fa7cSVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
524e212fa7cSVladimir Oltean 		if (dsa_port_offloads_lag(dp, info->lag))
525e212fa7cSVladimir Oltean 			return dsa_switch_do_lag_fdb_add(ds, info->lag,
526c2693363SVladimir Oltean 							 info->addr, info->vid,
527c2693363SVladimir Oltean 							 info->db);
528e212fa7cSVladimir Oltean 
529e212fa7cSVladimir Oltean 	return 0;
530e212fa7cSVladimir Oltean }
531e212fa7cSVladimir Oltean 
dsa_switch_lag_fdb_del(struct dsa_switch * ds,struct dsa_notifier_lag_fdb_info * info)532e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_del(struct dsa_switch *ds,
533e212fa7cSVladimir Oltean 				  struct dsa_notifier_lag_fdb_info *info)
534e212fa7cSVladimir Oltean {
535e212fa7cSVladimir Oltean 	struct dsa_port *dp;
536e212fa7cSVladimir Oltean 
537e212fa7cSVladimir Oltean 	if (!ds->ops->lag_fdb_del)
538e212fa7cSVladimir Oltean 		return -EOPNOTSUPP;
539e212fa7cSVladimir Oltean 
540e212fa7cSVladimir Oltean 	/* Notify switch only if it has a port in this LAG */
541e212fa7cSVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
542e212fa7cSVladimir Oltean 		if (dsa_port_offloads_lag(dp, info->lag))
543e212fa7cSVladimir Oltean 			return dsa_switch_do_lag_fdb_del(ds, info->lag,
544c2693363SVladimir Oltean 							 info->addr, info->vid,
545c2693363SVladimir Oltean 							 info->db);
546e212fa7cSVladimir Oltean 
547e212fa7cSVladimir Oltean 	return 0;
548e212fa7cSVladimir Oltean }
549e212fa7cSVladimir Oltean 
dsa_switch_lag_change(struct dsa_switch * ds,struct dsa_notifier_lag_info * info)550058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds,
551058102a6STobias Waldekranz 				 struct dsa_notifier_lag_info *info)
552058102a6STobias Waldekranz {
553726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_lag_change)
554726816a1SVladimir Oltean 		return ds->ops->port_lag_change(ds, info->dp->index);
555058102a6STobias Waldekranz 
556726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_lag_change)
557726816a1SVladimir Oltean 		return ds->ops->crosschip_lag_change(ds, info->dp->ds->index,
558726816a1SVladimir Oltean 						     info->dp->index);
559058102a6STobias Waldekranz 
560058102a6STobias Waldekranz 	return 0;
561058102a6STobias Waldekranz }
562058102a6STobias Waldekranz 
dsa_switch_lag_join(struct dsa_switch * ds,struct dsa_notifier_lag_info * info)563058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds,
564058102a6STobias Waldekranz 			       struct dsa_notifier_lag_info *info)
565058102a6STobias Waldekranz {
566726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_lag_join)
567726816a1SVladimir Oltean 		return ds->ops->port_lag_join(ds, info->dp->index, info->lag,
5682e359b00SVladimir Oltean 					      info->info, info->extack);
569058102a6STobias Waldekranz 
570726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_lag_join)
571726816a1SVladimir Oltean 		return ds->ops->crosschip_lag_join(ds, info->dp->ds->index,
572726816a1SVladimir Oltean 						   info->dp->index, info->lag,
5732e359b00SVladimir Oltean 						   info->info, info->extack);
574058102a6STobias Waldekranz 
575b71d0987SVladimir Oltean 	return -EOPNOTSUPP;
576058102a6STobias Waldekranz }
577058102a6STobias Waldekranz 
dsa_switch_lag_leave(struct dsa_switch * ds,struct dsa_notifier_lag_info * info)578058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds,
579058102a6STobias Waldekranz 				struct dsa_notifier_lag_info *info)
580058102a6STobias Waldekranz {
581726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_lag_leave)
582726816a1SVladimir Oltean 		return ds->ops->port_lag_leave(ds, info->dp->index, info->lag);
583058102a6STobias Waldekranz 
584726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_lag_leave)
585726816a1SVladimir Oltean 		return ds->ops->crosschip_lag_leave(ds, info->dp->ds->index,
586726816a1SVladimir Oltean 						    info->dp->index, info->lag);
587058102a6STobias Waldekranz 
588b71d0987SVladimir Oltean 	return -EOPNOTSUPP;
589058102a6STobias Waldekranz }
590058102a6STobias Waldekranz 
dsa_switch_mdb_add(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)591ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds,
592e65d45ccSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
593e6db98dbSVivien Didelot {
594726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
595fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
596e6db98dbSVivien Didelot 
597a52b2da7SVladimir Oltean 	if (!ds->ops->port_mdb_add)
598e6db98dbSVivien Didelot 		return -EOPNOTSUPP;
599e6db98dbSVivien Didelot 
600c2693363SVladimir Oltean 	return dsa_port_do_mdb_add(dp, info->mdb, info->db);
601e6db98dbSVivien Didelot }
6028ae5bcdcSVivien Didelot 
dsa_switch_mdb_del(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)6038ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds,
6048ae5bcdcSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
6058ae5bcdcSVivien Didelot {
606726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
607fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
608161ca59dSVladimir Oltean 
6098ae5bcdcSVivien Didelot 	if (!ds->ops->port_mdb_del)
6108ae5bcdcSVivien Didelot 		return -EOPNOTSUPP;
6118ae5bcdcSVivien Didelot 
612c2693363SVladimir Oltean 	return dsa_port_do_mdb_del(dp, info->mdb, info->db);
6138ae5bcdcSVivien Didelot }
6148ae5bcdcSVivien Didelot 
dsa_switch_host_mdb_add(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)615b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
616b8e997c4SVladimir Oltean 				   struct dsa_notifier_mdb_info *info)
617b8e997c4SVladimir Oltean {
618fac6abd5SVladimir Oltean 	struct dsa_port *dp;
619b8e997c4SVladimir Oltean 	int err = 0;
620b8e997c4SVladimir Oltean 
621b8e997c4SVladimir Oltean 	if (!ds->ops->port_mdb_add)
622b8e997c4SVladimir Oltean 		return -EOPNOTSUPP;
623b8e997c4SVladimir Oltean 
624fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
625726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
626c2693363SVladimir Oltean 			err = dsa_port_do_mdb_add(dp, info->mdb, info->db);
627b8e997c4SVladimir Oltean 			if (err)
628b8e997c4SVladimir Oltean 				break;
629b8e997c4SVladimir Oltean 		}
630b8e997c4SVladimir Oltean 	}
631b8e997c4SVladimir Oltean 
632b8e997c4SVladimir Oltean 	return err;
633b8e997c4SVladimir Oltean }
634b8e997c4SVladimir Oltean 
dsa_switch_host_mdb_del(struct dsa_switch * ds,struct dsa_notifier_mdb_info * info)635b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
636b8e997c4SVladimir Oltean 				   struct dsa_notifier_mdb_info *info)
637b8e997c4SVladimir Oltean {
638fac6abd5SVladimir Oltean 	struct dsa_port *dp;
639161ca59dSVladimir Oltean 	int err = 0;
640161ca59dSVladimir Oltean 
641b8e997c4SVladimir Oltean 	if (!ds->ops->port_mdb_del)
642b8e997c4SVladimir Oltean 		return -EOPNOTSUPP;
643b8e997c4SVladimir Oltean 
644fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
645726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
646c2693363SVladimir Oltean 			err = dsa_port_do_mdb_del(dp, info->mdb, info->db);
647161ca59dSVladimir Oltean 			if (err)
648161ca59dSVladimir Oltean 				break;
649161ca59dSVladimir Oltean 		}
650161ca59dSVladimir Oltean 	}
651b8e997c4SVladimir Oltean 
652161ca59dSVladimir Oltean 	return err;
653b8e997c4SVladimir Oltean }
654b8e997c4SVladimir Oltean 
655134ef238SVladimir Oltean /* Port VLANs match on the targeted port and on all DSA ports */
dsa_port_vlan_match(struct dsa_port * dp,struct dsa_notifier_vlan_info * info)656fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp,
657e65d45ccSVivien Didelot 				struct dsa_notifier_vlan_info *info)
658e65d45ccSVivien Didelot {
659726816a1SVladimir Oltean 	return dsa_port_is_dsa(dp) || dp == info->dp;
660e65d45ccSVivien Didelot }
661e65d45ccSVivien Didelot 
662134ef238SVladimir Oltean /* Host VLANs match on the targeted port's CPU port, and on all DSA ports
663134ef238SVladimir Oltean  * (upstream and downstream) of that switch and its upstream switches.
664134ef238SVladimir Oltean  */
dsa_port_host_vlan_match(struct dsa_port * dp,const struct dsa_port * targeted_dp)665134ef238SVladimir Oltean static bool dsa_port_host_vlan_match(struct dsa_port *dp,
666726816a1SVladimir Oltean 				     const struct dsa_port *targeted_dp)
667134ef238SVladimir Oltean {
668726816a1SVladimir Oltean 	struct dsa_port *cpu_dp = targeted_dp->cpu_dp;
669134ef238SVladimir Oltean 
670726816a1SVladimir Oltean 	if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds))
671134ef238SVladimir Oltean 		return dsa_port_is_dsa(dp) || dp == cpu_dp;
672134ef238SVladimir Oltean 
673134ef238SVladimir Oltean 	return false;
674134ef238SVladimir Oltean }
675134ef238SVladimir Oltean 
dsa_vlan_find(struct list_head * vlan_list,const struct switchdev_obj_port_vlan * vlan)676d06f925fSVladimir Oltean struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list,
677134ef238SVladimir Oltean 			       const struct switchdev_obj_port_vlan *vlan)
678134ef238SVladimir Oltean {
679134ef238SVladimir Oltean 	struct dsa_vlan *v;
680134ef238SVladimir Oltean 
681134ef238SVladimir Oltean 	list_for_each_entry(v, vlan_list, list)
682134ef238SVladimir Oltean 		if (v->vid == vlan->vid)
683134ef238SVladimir Oltean 			return v;
684134ef238SVladimir Oltean 
685134ef238SVladimir Oltean 	return NULL;
686134ef238SVladimir Oltean }
687134ef238SVladimir Oltean 
dsa_port_do_vlan_add(struct dsa_port * dp,const struct switchdev_obj_port_vlan * vlan,struct netlink_ext_ack * extack)688134ef238SVladimir Oltean static int dsa_port_do_vlan_add(struct dsa_port *dp,
689134ef238SVladimir Oltean 				const struct switchdev_obj_port_vlan *vlan,
690134ef238SVladimir Oltean 				struct netlink_ext_ack *extack)
691134ef238SVladimir Oltean {
692134ef238SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
693134ef238SVladimir Oltean 	int port = dp->index;
694134ef238SVladimir Oltean 	struct dsa_vlan *v;
695134ef238SVladimir Oltean 	int err = 0;
696134ef238SVladimir Oltean 
697134ef238SVladimir Oltean 	/* No need to bother with refcounting for user ports. */
69802020bd7SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
69902020bd7SVladimir Oltean 		err = ds->ops->port_vlan_add(ds, port, vlan, extack);
70002020bd7SVladimir Oltean 		trace_dsa_vlan_add_hw(dp, vlan, err);
70102020bd7SVladimir Oltean 
70202020bd7SVladimir Oltean 		return err;
70302020bd7SVladimir Oltean 	}
704134ef238SVladimir Oltean 
705134ef238SVladimir Oltean 	/* No need to propagate on shared ports the existing VLANs that were
706134ef238SVladimir Oltean 	 * re-notified after just the flags have changed. This would cause a
707134ef238SVladimir Oltean 	 * refcount bump which we need to avoid, since it unbalances the
708134ef238SVladimir Oltean 	 * additions with the deletions.
709134ef238SVladimir Oltean 	 */
710134ef238SVladimir Oltean 	if (vlan->changed)
711134ef238SVladimir Oltean 		return 0;
712134ef238SVladimir Oltean 
713134ef238SVladimir Oltean 	mutex_lock(&dp->vlans_lock);
714134ef238SVladimir Oltean 
715134ef238SVladimir Oltean 	v = dsa_vlan_find(&dp->vlans, vlan);
716134ef238SVladimir Oltean 	if (v) {
717134ef238SVladimir Oltean 		refcount_inc(&v->refcount);
71802020bd7SVladimir Oltean 		trace_dsa_vlan_add_bump(dp, vlan, &v->refcount);
719134ef238SVladimir Oltean 		goto out;
720134ef238SVladimir Oltean 	}
721134ef238SVladimir Oltean 
722134ef238SVladimir Oltean 	v = kzalloc(sizeof(*v), GFP_KERNEL);
723134ef238SVladimir Oltean 	if (!v) {
724134ef238SVladimir Oltean 		err = -ENOMEM;
725134ef238SVladimir Oltean 		goto out;
726134ef238SVladimir Oltean 	}
727134ef238SVladimir Oltean 
728134ef238SVladimir Oltean 	err = ds->ops->port_vlan_add(ds, port, vlan, extack);
72902020bd7SVladimir Oltean 	trace_dsa_vlan_add_hw(dp, vlan, err);
730134ef238SVladimir Oltean 	if (err) {
731134ef238SVladimir Oltean 		kfree(v);
732134ef238SVladimir Oltean 		goto out;
733134ef238SVladimir Oltean 	}
734134ef238SVladimir Oltean 
735134ef238SVladimir Oltean 	v->vid = vlan->vid;
736134ef238SVladimir Oltean 	refcount_set(&v->refcount, 1);
737134ef238SVladimir Oltean 	list_add_tail(&v->list, &dp->vlans);
738134ef238SVladimir Oltean 
739134ef238SVladimir Oltean out:
740134ef238SVladimir Oltean 	mutex_unlock(&dp->vlans_lock);
741134ef238SVladimir Oltean 
742134ef238SVladimir Oltean 	return err;
743134ef238SVladimir Oltean }
744134ef238SVladimir Oltean 
dsa_port_do_vlan_del(struct dsa_port * dp,const struct switchdev_obj_port_vlan * vlan)745134ef238SVladimir Oltean static int dsa_port_do_vlan_del(struct dsa_port *dp,
746134ef238SVladimir Oltean 				const struct switchdev_obj_port_vlan *vlan)
747134ef238SVladimir Oltean {
748134ef238SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
749134ef238SVladimir Oltean 	int port = dp->index;
750134ef238SVladimir Oltean 	struct dsa_vlan *v;
751134ef238SVladimir Oltean 	int err = 0;
752134ef238SVladimir Oltean 
753134ef238SVladimir Oltean 	/* No need to bother with refcounting for user ports */
75402020bd7SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
75502020bd7SVladimir Oltean 		err = ds->ops->port_vlan_del(ds, port, vlan);
75602020bd7SVladimir Oltean 		trace_dsa_vlan_del_hw(dp, vlan, err);
75702020bd7SVladimir Oltean 
75802020bd7SVladimir Oltean 		return err;
75902020bd7SVladimir Oltean 	}
760134ef238SVladimir Oltean 
761134ef238SVladimir Oltean 	mutex_lock(&dp->vlans_lock);
762134ef238SVladimir Oltean 
763134ef238SVladimir Oltean 	v = dsa_vlan_find(&dp->vlans, vlan);
764134ef238SVladimir Oltean 	if (!v) {
76502020bd7SVladimir Oltean 		trace_dsa_vlan_del_not_found(dp, vlan);
766134ef238SVladimir Oltean 		err = -ENOENT;
767134ef238SVladimir Oltean 		goto out;
768134ef238SVladimir Oltean 	}
769134ef238SVladimir Oltean 
77002020bd7SVladimir Oltean 	if (!refcount_dec_and_test(&v->refcount)) {
77102020bd7SVladimir Oltean 		trace_dsa_vlan_del_drop(dp, vlan, &v->refcount);
772134ef238SVladimir Oltean 		goto out;
77302020bd7SVladimir Oltean 	}
774134ef238SVladimir Oltean 
775134ef238SVladimir Oltean 	err = ds->ops->port_vlan_del(ds, port, vlan);
77602020bd7SVladimir Oltean 	trace_dsa_vlan_del_hw(dp, vlan, err);
777134ef238SVladimir Oltean 	if (err) {
778134ef238SVladimir Oltean 		refcount_set(&v->refcount, 1);
779134ef238SVladimir Oltean 		goto out;
780134ef238SVladimir Oltean 	}
781134ef238SVladimir Oltean 
782134ef238SVladimir Oltean 	list_del(&v->list);
783134ef238SVladimir Oltean 	kfree(v);
784134ef238SVladimir Oltean 
785134ef238SVladimir Oltean out:
786134ef238SVladimir Oltean 	mutex_unlock(&dp->vlans_lock);
787134ef238SVladimir Oltean 
788134ef238SVladimir Oltean 	return err;
789134ef238SVladimir Oltean }
790134ef238SVladimir Oltean 
dsa_switch_vlan_add(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)791ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds,
792e65d45ccSVivien Didelot 			       struct dsa_notifier_vlan_info *info)
7939c428c59SVivien Didelot {
794fac6abd5SVladimir Oltean 	struct dsa_port *dp;
795fac6abd5SVladimir Oltean 	int err;
7969c428c59SVivien Didelot 
7971958d581SVladimir Oltean 	if (!ds->ops->port_vlan_add)
7989c428c59SVivien Didelot 		return -EOPNOTSUPP;
7999c428c59SVivien Didelot 
800fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
801fac6abd5SVladimir Oltean 		if (dsa_port_vlan_match(dp, info)) {
802134ef238SVladimir Oltean 			err = dsa_port_do_vlan_add(dp, info->vlan,
80331046a5fSVladimir Oltean 						   info->extack);
8049c428c59SVivien Didelot 			if (err)
8059c428c59SVivien Didelot 				return err;
8069c428c59SVivien Didelot 		}
807e65d45ccSVivien Didelot 	}
8089c428c59SVivien Didelot 
809d0c627b8SVivien Didelot 	return 0;
810d0c627b8SVivien Didelot }
811d0c627b8SVivien Didelot 
dsa_switch_vlan_del(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)812d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds,
813d0c627b8SVivien Didelot 			       struct dsa_notifier_vlan_info *info)
814d0c627b8SVivien Didelot {
815134ef238SVladimir Oltean 	struct dsa_port *dp;
816134ef238SVladimir Oltean 	int err;
817134ef238SVladimir Oltean 
818d0c627b8SVivien Didelot 	if (!ds->ops->port_vlan_del)
819d0c627b8SVivien Didelot 		return -EOPNOTSUPP;
820d0c627b8SVivien Didelot 
821134ef238SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
822134ef238SVladimir Oltean 		if (dsa_port_vlan_match(dp, info)) {
823134ef238SVladimir Oltean 			err = dsa_port_do_vlan_del(dp, info->vlan);
824134ef238SVladimir Oltean 			if (err)
825134ef238SVladimir Oltean 				return err;
826134ef238SVladimir Oltean 		}
827134ef238SVladimir Oltean 	}
8281ca4aa9cSVivien Didelot 
829134ef238SVladimir Oltean 	return 0;
830134ef238SVladimir Oltean }
831134ef238SVladimir Oltean 
dsa_switch_host_vlan_add(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)832134ef238SVladimir Oltean static int dsa_switch_host_vlan_add(struct dsa_switch *ds,
833134ef238SVladimir Oltean 				    struct dsa_notifier_vlan_info *info)
834134ef238SVladimir Oltean {
835134ef238SVladimir Oltean 	struct dsa_port *dp;
836134ef238SVladimir Oltean 	int err;
837134ef238SVladimir Oltean 
838134ef238SVladimir Oltean 	if (!ds->ops->port_vlan_add)
839134ef238SVladimir Oltean 		return -EOPNOTSUPP;
840134ef238SVladimir Oltean 
841134ef238SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
842726816a1SVladimir Oltean 		if (dsa_port_host_vlan_match(dp, info->dp)) {
843134ef238SVladimir Oltean 			err = dsa_port_do_vlan_add(dp, info->vlan,
844134ef238SVladimir Oltean 						   info->extack);
845134ef238SVladimir Oltean 			if (err)
846134ef238SVladimir Oltean 				return err;
847134ef238SVladimir Oltean 		}
848134ef238SVladimir Oltean 	}
849134ef238SVladimir Oltean 
850134ef238SVladimir Oltean 	return 0;
851134ef238SVladimir Oltean }
852134ef238SVladimir Oltean 
dsa_switch_host_vlan_del(struct dsa_switch * ds,struct dsa_notifier_vlan_info * info)853134ef238SVladimir Oltean static int dsa_switch_host_vlan_del(struct dsa_switch *ds,
854134ef238SVladimir Oltean 				    struct dsa_notifier_vlan_info *info)
855134ef238SVladimir Oltean {
856134ef238SVladimir Oltean 	struct dsa_port *dp;
857134ef238SVladimir Oltean 	int err;
858134ef238SVladimir Oltean 
859134ef238SVladimir Oltean 	if (!ds->ops->port_vlan_del)
860134ef238SVladimir Oltean 		return -EOPNOTSUPP;
861134ef238SVladimir Oltean 
862134ef238SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
863726816a1SVladimir Oltean 		if (dsa_port_host_vlan_match(dp, info->dp)) {
864134ef238SVladimir Oltean 			err = dsa_port_do_vlan_del(dp, info->vlan);
865134ef238SVladimir Oltean 			if (err)
866134ef238SVladimir Oltean 				return err;
867134ef238SVladimir Oltean 		}
868134ef238SVladimir Oltean 	}
869134ef238SVladimir Oltean 
8701ca4aa9cSVivien Didelot 	return 0;
871d0c627b8SVivien Didelot }
872d0c627b8SVivien Didelot 
dsa_switch_change_tag_proto(struct dsa_switch * ds,struct dsa_notifier_tag_proto_info * info)87353da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
87453da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
87553da0ebaSVladimir Oltean {
87653da0ebaSVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
877d0004a02SVladimir Oltean 	struct dsa_port *dp, *cpu_dp;
878d0004a02SVladimir Oltean 	int err;
87953da0ebaSVladimir Oltean 
88053da0ebaSVladimir Oltean 	if (!ds->ops->change_tag_protocol)
88153da0ebaSVladimir Oltean 		return -EOPNOTSUPP;
88253da0ebaSVladimir Oltean 
88353da0ebaSVladimir Oltean 	ASSERT_RTNL();
88453da0ebaSVladimir Oltean 
885bacf93b0SVladimir Oltean 	err = ds->ops->change_tag_protocol(ds, tag_ops->proto);
88653da0ebaSVladimir Oltean 	if (err)
88753da0ebaSVladimir Oltean 		return err;
88853da0ebaSVladimir Oltean 
889bacf93b0SVladimir Oltean 	dsa_switch_for_each_cpu_port(cpu_dp, ds)
890d0004a02SVladimir Oltean 		dsa_port_set_tag_protocol(cpu_dp, tag_ops);
89153da0ebaSVladimir Oltean 
89253da0ebaSVladimir Oltean 	/* Now that changing the tag protocol can no longer fail, let's update
89353da0ebaSVladimir Oltean 	 * the remaining bits which are "duplicated for faster access", and the
89453da0ebaSVladimir Oltean 	 * bits that depend on the tagger, such as the MTU.
89553da0ebaSVladimir Oltean 	 */
896d0004a02SVladimir Oltean 	dsa_switch_for_each_user_port(dp, ds) {
897*6ca80638SFlorian Fainelli 		struct net_device *user = dp->user;
89853da0ebaSVladimir Oltean 
899*6ca80638SFlorian Fainelli 		dsa_user_setup_tagger(user);
90053da0ebaSVladimir Oltean 
90153da0ebaSVladimir Oltean 		/* rtnl_mutex is held in dsa_tree_change_tag_proto */
902*6ca80638SFlorian Fainelli 		dsa_user_change_mtu(user, user->mtu);
90353da0ebaSVladimir Oltean 	}
90453da0ebaSVladimir Oltean 
90553da0ebaSVladimir Oltean 	return 0;
90653da0ebaSVladimir Oltean }
90753da0ebaSVladimir Oltean 
9087f297314SVladimir Oltean /* We use the same cross-chip notifiers to inform both the tagger side, as well
9097f297314SVladimir Oltean  * as the switch side, of connection and disconnection events.
9107f297314SVladimir Oltean  * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the
9117f297314SVladimir Oltean  * switch side doesn't support connecting to this tagger, and therefore, the
9127f297314SVladimir Oltean  * fact that we don't disconnect the tagger side doesn't constitute a memory
9137f297314SVladimir Oltean  * leak: the tagger will still operate with persistent per-switch memory, just
9147f297314SVladimir Oltean  * with the switch side unconnected to it. What does constitute a hard error is
9157f297314SVladimir Oltean  * when the switch side supports connecting but fails.
9167f297314SVladimir Oltean  */
9177f297314SVladimir Oltean static int
dsa_switch_connect_tag_proto(struct dsa_switch * ds,struct dsa_notifier_tag_proto_info * info)9187f297314SVladimir Oltean dsa_switch_connect_tag_proto(struct dsa_switch *ds,
919dc452a47SVladimir Oltean 			     struct dsa_notifier_tag_proto_info *info)
920dc452a47SVladimir Oltean {
921dc452a47SVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
9227f297314SVladimir Oltean 	int err;
9237f297314SVladimir Oltean 
9247f297314SVladimir Oltean 	/* Notify the new tagger about the connection to this switch */
9257f297314SVladimir Oltean 	if (tag_ops->connect) {
9267f297314SVladimir Oltean 		err = tag_ops->connect(ds);
9277f297314SVladimir Oltean 		if (err)
9287f297314SVladimir Oltean 			return err;
9297f297314SVladimir Oltean 	}
930dc452a47SVladimir Oltean 
931dc452a47SVladimir Oltean 	if (!ds->ops->connect_tag_protocol)
932dc452a47SVladimir Oltean 		return -EOPNOTSUPP;
933dc452a47SVladimir Oltean 
9347f297314SVladimir Oltean 	/* Notify the switch about the connection to the new tagger */
9357f297314SVladimir Oltean 	err = ds->ops->connect_tag_protocol(ds, tag_ops->proto);
9367f297314SVladimir Oltean 	if (err) {
9377f297314SVladimir Oltean 		/* Revert the new tagger's connection to this tree */
9387f297314SVladimir Oltean 		if (tag_ops->disconnect)
9397f297314SVladimir Oltean 			tag_ops->disconnect(ds);
9407f297314SVladimir Oltean 		return err;
9417f297314SVladimir Oltean 	}
9427f297314SVladimir Oltean 
9437f297314SVladimir Oltean 	return 0;
9447f297314SVladimir Oltean }
9457f297314SVladimir Oltean 
9467f297314SVladimir Oltean static int
dsa_switch_disconnect_tag_proto(struct dsa_switch * ds,struct dsa_notifier_tag_proto_info * info)9477f297314SVladimir Oltean dsa_switch_disconnect_tag_proto(struct dsa_switch *ds,
9487f297314SVladimir Oltean 				struct dsa_notifier_tag_proto_info *info)
9497f297314SVladimir Oltean {
9507f297314SVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
9517f297314SVladimir Oltean 
9527f297314SVladimir Oltean 	/* Notify the tagger about the disconnection from this switch */
9537f297314SVladimir Oltean 	if (tag_ops->disconnect && ds->tagger_data)
9547f297314SVladimir Oltean 		tag_ops->disconnect(ds);
9557f297314SVladimir Oltean 
9567f297314SVladimir Oltean 	/* No need to notify the switch, since it shouldn't have any
9577f297314SVladimir Oltean 	 * resources to tear down
9587f297314SVladimir Oltean 	 */
9597f297314SVladimir Oltean 	return 0;
960dc452a47SVladimir Oltean }
961dc452a47SVladimir Oltean 
962295ab96fSVladimir Oltean static int
dsa_switch_conduit_state_change(struct dsa_switch * ds,struct dsa_notifier_conduit_state_info * info)963*6ca80638SFlorian Fainelli dsa_switch_conduit_state_change(struct dsa_switch *ds,
964*6ca80638SFlorian Fainelli 				struct dsa_notifier_conduit_state_info *info)
965295ab96fSVladimir Oltean {
966*6ca80638SFlorian Fainelli 	if (!ds->ops->conduit_state_change)
967295ab96fSVladimir Oltean 		return 0;
968295ab96fSVladimir Oltean 
969*6ca80638SFlorian Fainelli 	ds->ops->conduit_state_change(ds, info->conduit, info->operational);
970295ab96fSVladimir Oltean 
971295ab96fSVladimir Oltean 	return 0;
972295ab96fSVladimir Oltean }
973295ab96fSVladimir Oltean 
dsa_switch_event(struct notifier_block * nb,unsigned long event,void * info)974f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb,
975f515f192SVivien Didelot 			    unsigned long event, void *info)
976f515f192SVivien Didelot {
977f515f192SVivien Didelot 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
978f515f192SVivien Didelot 	int err;
979f515f192SVivien Didelot 
980f515f192SVivien Didelot 	switch (event) {
9811faabf74SVivien Didelot 	case DSA_NOTIFIER_AGEING_TIME:
9821faabf74SVivien Didelot 		err = dsa_switch_ageing_time(ds, info);
9831faabf74SVivien Didelot 		break;
98404d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_JOIN:
98504d3a4c6SVivien Didelot 		err = dsa_switch_bridge_join(ds, info);
98604d3a4c6SVivien Didelot 		break;
98704d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_LEAVE:
98804d3a4c6SVivien Didelot 		err = dsa_switch_bridge_leave(ds, info);
98904d3a4c6SVivien Didelot 		break;
990685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_ADD:
991685fb6a4SVivien Didelot 		err = dsa_switch_fdb_add(ds, info);
992685fb6a4SVivien Didelot 		break;
993685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_DEL:
994685fb6a4SVivien Didelot 		err = dsa_switch_fdb_del(ds, info);
995685fb6a4SVivien Didelot 		break;
9963dc80afcSVladimir Oltean 	case DSA_NOTIFIER_HOST_FDB_ADD:
9973dc80afcSVladimir Oltean 		err = dsa_switch_host_fdb_add(ds, info);
9983dc80afcSVladimir Oltean 		break;
9993dc80afcSVladimir Oltean 	case DSA_NOTIFIER_HOST_FDB_DEL:
10003dc80afcSVladimir Oltean 		err = dsa_switch_host_fdb_del(ds, info);
10013dc80afcSVladimir Oltean 		break;
1002e212fa7cSVladimir Oltean 	case DSA_NOTIFIER_LAG_FDB_ADD:
1003e212fa7cSVladimir Oltean 		err = dsa_switch_lag_fdb_add(ds, info);
1004e212fa7cSVladimir Oltean 		break;
1005e212fa7cSVladimir Oltean 	case DSA_NOTIFIER_LAG_FDB_DEL:
1006e212fa7cSVladimir Oltean 		err = dsa_switch_lag_fdb_del(ds, info);
1007e212fa7cSVladimir Oltean 		break;
1008058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_CHANGE:
1009058102a6STobias Waldekranz 		err = dsa_switch_lag_change(ds, info);
1010058102a6STobias Waldekranz 		break;
1011058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_JOIN:
1012058102a6STobias Waldekranz 		err = dsa_switch_lag_join(ds, info);
1013058102a6STobias Waldekranz 		break;
1014058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_LEAVE:
1015058102a6STobias Waldekranz 		err = dsa_switch_lag_leave(ds, info);
1016058102a6STobias Waldekranz 		break;
10178ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_ADD:
10188ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_add(ds, info);
10198ae5bcdcSVivien Didelot 		break;
10208ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_DEL:
10218ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_del(ds, info);
10228ae5bcdcSVivien Didelot 		break;
1023b8e997c4SVladimir Oltean 	case DSA_NOTIFIER_HOST_MDB_ADD:
1024b8e997c4SVladimir Oltean 		err = dsa_switch_host_mdb_add(ds, info);
1025b8e997c4SVladimir Oltean 		break;
1026b8e997c4SVladimir Oltean 	case DSA_NOTIFIER_HOST_MDB_DEL:
1027b8e997c4SVladimir Oltean 		err = dsa_switch_host_mdb_del(ds, info);
1028b8e997c4SVladimir Oltean 		break;
1029d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_ADD:
1030d0c627b8SVivien Didelot 		err = dsa_switch_vlan_add(ds, info);
1031d0c627b8SVivien Didelot 		break;
1032d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_DEL:
1033d0c627b8SVivien Didelot 		err = dsa_switch_vlan_del(ds, info);
1034d0c627b8SVivien Didelot 		break;
1035134ef238SVladimir Oltean 	case DSA_NOTIFIER_HOST_VLAN_ADD:
1036134ef238SVladimir Oltean 		err = dsa_switch_host_vlan_add(ds, info);
1037134ef238SVladimir Oltean 		break;
1038134ef238SVladimir Oltean 	case DSA_NOTIFIER_HOST_VLAN_DEL:
1039134ef238SVladimir Oltean 		err = dsa_switch_host_vlan_del(ds, info);
1040134ef238SVladimir Oltean 		break;
1041bfcb8132SVladimir Oltean 	case DSA_NOTIFIER_MTU:
1042bfcb8132SVladimir Oltean 		err = dsa_switch_mtu(ds, info);
1043bfcb8132SVladimir Oltean 		break;
104453da0ebaSVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO:
104553da0ebaSVladimir Oltean 		err = dsa_switch_change_tag_proto(ds, info);
104653da0ebaSVladimir Oltean 		break;
1047dc452a47SVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO_CONNECT:
1048dc452a47SVladimir Oltean 		err = dsa_switch_connect_tag_proto(ds, info);
1049dc452a47SVladimir Oltean 		break;
10507f297314SVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO_DISCONNECT:
10517f297314SVladimir Oltean 		err = dsa_switch_disconnect_tag_proto(ds, info);
10527f297314SVladimir Oltean 		break;
1053c64b9c05SVladimir Oltean 	case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD:
1054c64b9c05SVladimir Oltean 		err = dsa_switch_tag_8021q_vlan_add(ds, info);
1055c64b9c05SVladimir Oltean 		break;
1056c64b9c05SVladimir Oltean 	case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
1057c64b9c05SVladimir Oltean 		err = dsa_switch_tag_8021q_vlan_del(ds, info);
1058c64b9c05SVladimir Oltean 		break;
1059*6ca80638SFlorian Fainelli 	case DSA_NOTIFIER_CONDUIT_STATE_CHANGE:
1060*6ca80638SFlorian Fainelli 		err = dsa_switch_conduit_state_change(ds, info);
1061295ab96fSVladimir Oltean 		break;
1062f515f192SVivien Didelot 	default:
1063f515f192SVivien Didelot 		err = -EOPNOTSUPP;
1064f515f192SVivien Didelot 		break;
1065f515f192SVivien Didelot 	}
1066f515f192SVivien Didelot 
1067f515f192SVivien Didelot 	if (err)
1068f515f192SVivien Didelot 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
1069f515f192SVivien Didelot 			event, err);
1070f515f192SVivien Didelot 
1071f515f192SVivien Didelot 	return notifier_from_errno(err);
1072f515f192SVivien Didelot }
1073f515f192SVivien Didelot 
10746dbdfce7SVladimir Oltean /**
10756dbdfce7SVladimir Oltean  * dsa_tree_notify - Execute code for all switches in a DSA switch tree.
10766dbdfce7SVladimir Oltean  * @dst: collection of struct dsa_switch devices to notify.
10776dbdfce7SVladimir Oltean  * @e: event, must be of type DSA_NOTIFIER_*
10786dbdfce7SVladimir Oltean  * @v: event-specific value.
10796dbdfce7SVladimir Oltean  *
10806dbdfce7SVladimir Oltean  * Given a struct dsa_switch_tree, this can be used to run a function once for
10816dbdfce7SVladimir Oltean  * each member DSA switch. The other alternative of traversing the tree is only
10826dbdfce7SVladimir Oltean  * through its ports list, which does not uniquely list the switches.
10836dbdfce7SVladimir Oltean  */
dsa_tree_notify(struct dsa_switch_tree * dst,unsigned long e,void * v)10846dbdfce7SVladimir Oltean int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v)
10856dbdfce7SVladimir Oltean {
10866dbdfce7SVladimir Oltean 	struct raw_notifier_head *nh = &dst->nh;
10876dbdfce7SVladimir Oltean 	int err;
10886dbdfce7SVladimir Oltean 
10896dbdfce7SVladimir Oltean 	err = raw_notifier_call_chain(nh, e, v);
10906dbdfce7SVladimir Oltean 
10916dbdfce7SVladimir Oltean 	return notifier_to_errno(err);
10926dbdfce7SVladimir Oltean }
10936dbdfce7SVladimir Oltean 
10946dbdfce7SVladimir Oltean /**
10956dbdfce7SVladimir Oltean  * dsa_broadcast - Notify all DSA trees in the system.
10966dbdfce7SVladimir Oltean  * @e: event, must be of type DSA_NOTIFIER_*
10976dbdfce7SVladimir Oltean  * @v: event-specific value.
10986dbdfce7SVladimir Oltean  *
10996dbdfce7SVladimir Oltean  * Can be used to notify the switching fabric of events such as cross-chip
11006dbdfce7SVladimir Oltean  * bridging between disjoint trees (such as islands of tagger-compatible
11016dbdfce7SVladimir Oltean  * switches bridged by an incompatible middle switch).
11026dbdfce7SVladimir Oltean  *
11036dbdfce7SVladimir Oltean  * WARNING: this function is not reliable during probe time, because probing
11046dbdfce7SVladimir Oltean  * between trees is asynchronous and not all DSA trees might have probed.
11056dbdfce7SVladimir Oltean  */
dsa_broadcast(unsigned long e,void * v)11066dbdfce7SVladimir Oltean int dsa_broadcast(unsigned long e, void *v)
11076dbdfce7SVladimir Oltean {
11086dbdfce7SVladimir Oltean 	struct dsa_switch_tree *dst;
11096dbdfce7SVladimir Oltean 	int err = 0;
11106dbdfce7SVladimir Oltean 
11116dbdfce7SVladimir Oltean 	list_for_each_entry(dst, &dsa_tree_list, list) {
11126dbdfce7SVladimir Oltean 		err = dsa_tree_notify(dst, e, v);
11136dbdfce7SVladimir Oltean 		if (err)
11146dbdfce7SVladimir Oltean 			break;
11156dbdfce7SVladimir Oltean 	}
11166dbdfce7SVladimir Oltean 
11176dbdfce7SVladimir Oltean 	return err;
11186dbdfce7SVladimir Oltean }
11196dbdfce7SVladimir Oltean 
dsa_switch_register_notifier(struct dsa_switch * ds)1120f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds)
1121f515f192SVivien Didelot {
1122f515f192SVivien Didelot 	ds->nb.notifier_call = dsa_switch_event;
1123f515f192SVivien Didelot 
1124f515f192SVivien Didelot 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
1125f515f192SVivien Didelot }
1126f515f192SVivien Didelot 
dsa_switch_unregister_notifier(struct dsa_switch * ds)1127f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds)
1128f515f192SVivien Didelot {
1129f515f192SVivien Didelot 	int err;
1130f515f192SVivien Didelot 
1131f515f192SVivien Didelot 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
1132f515f192SVivien Didelot 	if (err)
1133f515f192SVivien Didelot 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
1134f515f192SVivien Didelot }
1135