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"
1809f92341SVladimir Oltean #include "slave.h"
190c603136SVladimir Oltean #include "switch.h"
2019d05ea7SVladimir Oltean #include "tag_8021q.h"
219538ebceSVladimir Oltean #include "trace.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)676134ef238SVladimir 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. */
698*02020bd7SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
699*02020bd7SVladimir Oltean err = ds->ops->port_vlan_add(ds, port, vlan, extack);
700*02020bd7SVladimir Oltean trace_dsa_vlan_add_hw(dp, vlan, err);
701*02020bd7SVladimir Oltean
702*02020bd7SVladimir Oltean return err;
703*02020bd7SVladimir 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);
718*02020bd7SVladimir 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);
729*02020bd7SVladimir 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 */
754*02020bd7SVladimir Oltean if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) {
755*02020bd7SVladimir Oltean err = ds->ops->port_vlan_del(ds, port, vlan);
756*02020bd7SVladimir Oltean trace_dsa_vlan_del_hw(dp, vlan, err);
757*02020bd7SVladimir Oltean
758*02020bd7SVladimir Oltean return err;
759*02020bd7SVladimir 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) {
765*02020bd7SVladimir Oltean trace_dsa_vlan_del_not_found(dp, vlan);
766134ef238SVladimir Oltean err = -ENOENT;
767134ef238SVladimir Oltean goto out;
768134ef238SVladimir Oltean }
769134ef238SVladimir Oltean
770*02020bd7SVladimir Oltean if (!refcount_dec_and_test(&v->refcount)) {
771*02020bd7SVladimir Oltean trace_dsa_vlan_del_drop(dp, vlan, &v->refcount);
772134ef238SVladimir Oltean goto out;
773*02020bd7SVladimir Oltean }
774134ef238SVladimir Oltean
775134ef238SVladimir Oltean err = ds->ops->port_vlan_del(ds, port, vlan);
776*02020bd7SVladimir 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) {
897d0004a02SVladimir Oltean struct net_device *slave = dp->slave;
89853da0ebaSVladimir Oltean
89953da0ebaSVladimir Oltean dsa_slave_setup_tagger(slave);
90053da0ebaSVladimir Oltean
90153da0ebaSVladimir Oltean /* rtnl_mutex is held in dsa_tree_change_tag_proto */
90253da0ebaSVladimir Oltean dsa_slave_change_mtu(slave, slave->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_master_state_change(struct dsa_switch * ds,struct dsa_notifier_master_state_info * info)963295ab96fSVladimir Oltean dsa_switch_master_state_change(struct dsa_switch *ds,
964295ab96fSVladimir Oltean struct dsa_notifier_master_state_info *info)
965295ab96fSVladimir Oltean {
966295ab96fSVladimir Oltean if (!ds->ops->master_state_change)
967295ab96fSVladimir Oltean return 0;
968295ab96fSVladimir Oltean
969295ab96fSVladimir Oltean ds->ops->master_state_change(ds, info->master, 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;
1059295ab96fSVladimir Oltean case DSA_NOTIFIER_MASTER_STATE_CHANGE:
1060295ab96fSVladimir Oltean err = dsa_switch_master_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