xref: /linux/net/dsa/switch.c (revision 47d2ce03dcfb6b7f0373aac6c667715d94caba78)
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 
15*47d2ce03SVladimir Oltean #include "dsa.h"
16ea5dd34bSVivien Didelot #include "dsa_priv.h"
17022bba63SVladimir Oltean #include "port.h"
1809f92341SVladimir Oltean #include "slave.h"
190c603136SVladimir Oltean #include "switch.h"
20f515f192SVivien Didelot 
211faabf74SVivien Didelot static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
221faabf74SVivien Didelot 						   unsigned int ageing_time)
231faabf74SVivien Didelot {
24d0004a02SVladimir Oltean 	struct dsa_port *dp;
251faabf74SVivien Didelot 
26d0004a02SVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
271faabf74SVivien Didelot 		if (dp->ageing_time && dp->ageing_time < ageing_time)
281faabf74SVivien Didelot 			ageing_time = dp->ageing_time;
291faabf74SVivien Didelot 
301faabf74SVivien Didelot 	return ageing_time;
311faabf74SVivien Didelot }
321faabf74SVivien Didelot 
331faabf74SVivien Didelot static int dsa_switch_ageing_time(struct dsa_switch *ds,
341faabf74SVivien Didelot 				  struct dsa_notifier_ageing_time_info *info)
351faabf74SVivien Didelot {
361faabf74SVivien Didelot 	unsigned int ageing_time = info->ageing_time;
371faabf74SVivien Didelot 
381faabf74SVivien Didelot 	if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
391faabf74SVivien Didelot 		return -ERANGE;
4077b61365SVladimir Oltean 
411faabf74SVivien Didelot 	if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
421faabf74SVivien Didelot 		return -ERANGE;
431faabf74SVivien Didelot 
441faabf74SVivien Didelot 	/* Program the fastest ageing time in case of multiple bridges */
451faabf74SVivien Didelot 	ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
461faabf74SVivien Didelot 
471faabf74SVivien Didelot 	if (ds->ops->set_ageing_time)
481faabf74SVivien Didelot 		return ds->ops->set_ageing_time(ds, ageing_time);
491faabf74SVivien Didelot 
501faabf74SVivien Didelot 	return 0;
511faabf74SVivien Didelot }
521faabf74SVivien Didelot 
53fac6abd5SVladimir Oltean static bool dsa_port_mtu_match(struct dsa_port *dp,
54bfcb8132SVladimir Oltean 			       struct dsa_notifier_mtu_info *info)
55bfcb8132SVladimir Oltean {
56be6ff966SVladimir Oltean 	return dp == info->dp || dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp);
57bfcb8132SVladimir Oltean }
58bfcb8132SVladimir Oltean 
59bfcb8132SVladimir Oltean static int dsa_switch_mtu(struct dsa_switch *ds,
60bfcb8132SVladimir Oltean 			  struct dsa_notifier_mtu_info *info)
61bfcb8132SVladimir Oltean {
62fac6abd5SVladimir Oltean 	struct dsa_port *dp;
63fac6abd5SVladimir Oltean 	int ret;
64bfcb8132SVladimir Oltean 
65bfcb8132SVladimir Oltean 	if (!ds->ops->port_change_mtu)
66bfcb8132SVladimir Oltean 		return -EOPNOTSUPP;
67bfcb8132SVladimir Oltean 
68fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
69fac6abd5SVladimir Oltean 		if (dsa_port_mtu_match(dp, info)) {
70fac6abd5SVladimir Oltean 			ret = ds->ops->port_change_mtu(ds, dp->index,
71fac6abd5SVladimir Oltean 						       info->mtu);
72bfcb8132SVladimir Oltean 			if (ret)
73bfcb8132SVladimir Oltean 				return ret;
74bfcb8132SVladimir Oltean 		}
75bfcb8132SVladimir Oltean 	}
76bfcb8132SVladimir Oltean 
77bfcb8132SVladimir Oltean 	return 0;
78bfcb8132SVladimir Oltean }
79bfcb8132SVladimir Oltean 
8004d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds,
8104d3a4c6SVivien Didelot 				  struct dsa_notifier_bridge_info *info)
8204d3a4c6SVivien Didelot {
83e19cc13cSVladimir Oltean 	int err;
84f66a6a69SVladimir Oltean 
85726816a1SVladimir Oltean 	if (info->dp->ds == ds) {
8667b5fb5dSVladimir Oltean 		if (!ds->ops->port_bridge_join)
8767b5fb5dSVladimir Oltean 			return -EOPNOTSUPP;
8867b5fb5dSVladimir Oltean 
89726816a1SVladimir Oltean 		err = ds->ops->port_bridge_join(ds, info->dp->index,
90726816a1SVladimir Oltean 						info->bridge,
9106b9cce4SVladimir Oltean 						&info->tx_fwd_offload,
9206b9cce4SVladimir Oltean 						info->extack);
93e19cc13cSVladimir Oltean 		if (err)
94e19cc13cSVladimir Oltean 			return err;
95e19cc13cSVladimir Oltean 	}
9604d3a4c6SVivien Didelot 
97726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_bridge_join) {
98726816a1SVladimir Oltean 		err = ds->ops->crosschip_bridge_join(ds,
99726816a1SVladimir Oltean 						     info->dp->ds->dst->index,
100726816a1SVladimir Oltean 						     info->dp->ds->index,
101726816a1SVladimir Oltean 						     info->dp->index,
102726816a1SVladimir Oltean 						     info->bridge,
10306b9cce4SVladimir Oltean 						     info->extack);
104e19cc13cSVladimir Oltean 		if (err)
105e19cc13cSVladimir Oltean 			return err;
106e19cc13cSVladimir Oltean 	}
10704d3a4c6SVivien Didelot 
10891495f21SVladimir Oltean 	return 0;
10904d3a4c6SVivien Didelot }
11004d3a4c6SVivien Didelot 
111381a7301STobias Waldekranz static int dsa_switch_bridge_leave(struct dsa_switch *ds,
112381a7301STobias Waldekranz 				   struct dsa_notifier_bridge_info *info)
113381a7301STobias Waldekranz {
114726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_bridge_leave)
115726816a1SVladimir Oltean 		ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge);
116381a7301STobias Waldekranz 
117726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_bridge_leave)
118726816a1SVladimir Oltean 		ds->ops->crosschip_bridge_leave(ds, info->dp->ds->dst->index,
119726816a1SVladimir Oltean 						info->dp->ds->index,
120726816a1SVladimir Oltean 						info->dp->index,
121381a7301STobias Waldekranz 						info->bridge);
122381a7301STobias Waldekranz 
12391495f21SVladimir Oltean 	return 0;
12404d3a4c6SVivien Didelot }
12504d3a4c6SVivien Didelot 
126b8e997c4SVladimir Oltean /* Matches for all upstream-facing ports (the CPU port and all upstream-facing
127b8e997c4SVladimir Oltean  * DSA links) that sit between the targeted port on which the notifier was
128b8e997c4SVladimir Oltean  * emitted and its dedicated CPU port.
129b8e997c4SVladimir Oltean  */
130fac6abd5SVladimir Oltean static bool dsa_port_host_address_match(struct dsa_port *dp,
131726816a1SVladimir Oltean 					const struct dsa_port *targeted_dp)
132b8e997c4SVladimir Oltean {
133726816a1SVladimir Oltean 	struct dsa_port *cpu_dp = targeted_dp->cpu_dp;
134b8e997c4SVladimir Oltean 
135726816a1SVladimir Oltean 	if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds))
136fac6abd5SVladimir Oltean 		return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index,
137b8e997c4SVladimir Oltean 						     cpu_dp->index);
138b8e997c4SVladimir Oltean 
139b8e997c4SVladimir Oltean 	return false;
140b8e997c4SVladimir Oltean }
141b8e997c4SVladimir Oltean 
142161ca59dSVladimir Oltean static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list,
143c2693363SVladimir Oltean 					      const unsigned char *addr, u16 vid,
144c2693363SVladimir Oltean 					      struct dsa_db db)
145161ca59dSVladimir Oltean {
146161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
147161ca59dSVladimir Oltean 
148161ca59dSVladimir Oltean 	list_for_each_entry(a, addr_list, list)
149c2693363SVladimir Oltean 		if (ether_addr_equal(a->addr, addr) && a->vid == vid &&
150c2693363SVladimir Oltean 		    dsa_db_equal(&a->db, &db))
151161ca59dSVladimir Oltean 			return a;
152161ca59dSVladimir Oltean 
153161ca59dSVladimir Oltean 	return NULL;
154161ca59dSVladimir Oltean }
155161ca59dSVladimir Oltean 
156fac6abd5SVladimir Oltean static int dsa_port_do_mdb_add(struct dsa_port *dp,
157c2693363SVladimir Oltean 			       const struct switchdev_obj_port_mdb *mdb,
158c2693363SVladimir Oltean 			       struct dsa_db db)
159161ca59dSVladimir Oltean {
160fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
161161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
162fac6abd5SVladimir Oltean 	int port = dp->index;
163338a3a47SVladimir Oltean 	int err = 0;
164161ca59dSVladimir Oltean 
165161ca59dSVladimir Oltean 	/* No need to bother with refcounting for user ports */
166161ca59dSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
167c2693363SVladimir Oltean 		return ds->ops->port_mdb_add(ds, port, mdb, db);
168161ca59dSVladimir Oltean 
169338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
170338a3a47SVladimir Oltean 
171c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);
172161ca59dSVladimir Oltean 	if (a) {
173161ca59dSVladimir Oltean 		refcount_inc(&a->refcount);
174338a3a47SVladimir Oltean 		goto out;
175161ca59dSVladimir Oltean 	}
176161ca59dSVladimir Oltean 
177161ca59dSVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
178338a3a47SVladimir Oltean 	if (!a) {
179338a3a47SVladimir Oltean 		err = -ENOMEM;
180338a3a47SVladimir Oltean 		goto out;
181338a3a47SVladimir Oltean 	}
182161ca59dSVladimir Oltean 
183c2693363SVladimir Oltean 	err = ds->ops->port_mdb_add(ds, port, mdb, db);
184161ca59dSVladimir Oltean 	if (err) {
185161ca59dSVladimir Oltean 		kfree(a);
186338a3a47SVladimir Oltean 		goto out;
187161ca59dSVladimir Oltean 	}
188161ca59dSVladimir Oltean 
189161ca59dSVladimir Oltean 	ether_addr_copy(a->addr, mdb->addr);
190161ca59dSVladimir Oltean 	a->vid = mdb->vid;
191c2693363SVladimir Oltean 	a->db = db;
192161ca59dSVladimir Oltean 	refcount_set(&a->refcount, 1);
193161ca59dSVladimir Oltean 	list_add_tail(&a->list, &dp->mdbs);
194161ca59dSVladimir Oltean 
195338a3a47SVladimir Oltean out:
196338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
197338a3a47SVladimir Oltean 
198338a3a47SVladimir Oltean 	return err;
199161ca59dSVladimir Oltean }
200161ca59dSVladimir Oltean 
201fac6abd5SVladimir Oltean static int dsa_port_do_mdb_del(struct dsa_port *dp,
202c2693363SVladimir Oltean 			       const struct switchdev_obj_port_mdb *mdb,
203c2693363SVladimir Oltean 			       struct dsa_db db)
204161ca59dSVladimir Oltean {
205fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
206161ca59dSVladimir Oltean 	struct dsa_mac_addr *a;
207fac6abd5SVladimir Oltean 	int port = dp->index;
208338a3a47SVladimir Oltean 	int err = 0;
209161ca59dSVladimir Oltean 
210161ca59dSVladimir Oltean 	/* No need to bother with refcounting for user ports */
211161ca59dSVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
212c2693363SVladimir Oltean 		return ds->ops->port_mdb_del(ds, port, mdb, db);
213161ca59dSVladimir Oltean 
214338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
215338a3a47SVladimir Oltean 
216c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db);
217338a3a47SVladimir Oltean 	if (!a) {
218338a3a47SVladimir Oltean 		err = -ENOENT;
219338a3a47SVladimir Oltean 		goto out;
220338a3a47SVladimir Oltean 	}
221161ca59dSVladimir Oltean 
222161ca59dSVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount))
223338a3a47SVladimir Oltean 		goto out;
224161ca59dSVladimir Oltean 
225c2693363SVladimir Oltean 	err = ds->ops->port_mdb_del(ds, port, mdb, db);
226161ca59dSVladimir Oltean 	if (err) {
227232deb3fSVladimir Oltean 		refcount_set(&a->refcount, 1);
228338a3a47SVladimir Oltean 		goto out;
229161ca59dSVladimir Oltean 	}
230161ca59dSVladimir Oltean 
231161ca59dSVladimir Oltean 	list_del(&a->list);
232161ca59dSVladimir Oltean 	kfree(a);
233161ca59dSVladimir Oltean 
234338a3a47SVladimir Oltean out:
235338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
236338a3a47SVladimir Oltean 
237338a3a47SVladimir Oltean 	return err;
238161ca59dSVladimir Oltean }
239161ca59dSVladimir Oltean 
240fac6abd5SVladimir Oltean static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
241c2693363SVladimir Oltean 			       u16 vid, struct dsa_db db)
2423f6e32f9SVladimir Oltean {
243fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
2443f6e32f9SVladimir Oltean 	struct dsa_mac_addr *a;
245fac6abd5SVladimir Oltean 	int port = dp->index;
246338a3a47SVladimir Oltean 	int err = 0;
2473f6e32f9SVladimir Oltean 
2483f6e32f9SVladimir Oltean 	/* No need to bother with refcounting for user ports */
2493f6e32f9SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
250c2693363SVladimir Oltean 		return ds->ops->port_fdb_add(ds, port, addr, vid, db);
2513f6e32f9SVladimir Oltean 
252338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
253338a3a47SVladimir Oltean 
254c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);
2553f6e32f9SVladimir Oltean 	if (a) {
2563f6e32f9SVladimir Oltean 		refcount_inc(&a->refcount);
257338a3a47SVladimir Oltean 		goto out;
2583f6e32f9SVladimir Oltean 	}
2593f6e32f9SVladimir Oltean 
2603f6e32f9SVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
261338a3a47SVladimir Oltean 	if (!a) {
262338a3a47SVladimir Oltean 		err = -ENOMEM;
263338a3a47SVladimir Oltean 		goto out;
264338a3a47SVladimir Oltean 	}
2653f6e32f9SVladimir Oltean 
266c2693363SVladimir Oltean 	err = ds->ops->port_fdb_add(ds, port, addr, vid, db);
2673f6e32f9SVladimir Oltean 	if (err) {
2683f6e32f9SVladimir Oltean 		kfree(a);
269338a3a47SVladimir Oltean 		goto out;
2703f6e32f9SVladimir Oltean 	}
2713f6e32f9SVladimir Oltean 
2723f6e32f9SVladimir Oltean 	ether_addr_copy(a->addr, addr);
2733f6e32f9SVladimir Oltean 	a->vid = vid;
274c2693363SVladimir Oltean 	a->db = db;
2753f6e32f9SVladimir Oltean 	refcount_set(&a->refcount, 1);
2763f6e32f9SVladimir Oltean 	list_add_tail(&a->list, &dp->fdbs);
2773f6e32f9SVladimir Oltean 
278338a3a47SVladimir Oltean out:
279338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
280338a3a47SVladimir Oltean 
281338a3a47SVladimir Oltean 	return err;
2823f6e32f9SVladimir Oltean }
2833f6e32f9SVladimir Oltean 
284fac6abd5SVladimir Oltean static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
285c2693363SVladimir Oltean 			       u16 vid, struct dsa_db db)
2863f6e32f9SVladimir Oltean {
287fac6abd5SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
2883f6e32f9SVladimir Oltean 	struct dsa_mac_addr *a;
289fac6abd5SVladimir Oltean 	int port = dp->index;
290338a3a47SVladimir Oltean 	int err = 0;
2913f6e32f9SVladimir Oltean 
2923f6e32f9SVladimir Oltean 	/* No need to bother with refcounting for user ports */
2933f6e32f9SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
294c2693363SVladimir Oltean 		return ds->ops->port_fdb_del(ds, port, addr, vid, db);
2953f6e32f9SVladimir Oltean 
296338a3a47SVladimir Oltean 	mutex_lock(&dp->addr_lists_lock);
297338a3a47SVladimir Oltean 
298c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db);
299338a3a47SVladimir Oltean 	if (!a) {
300338a3a47SVladimir Oltean 		err = -ENOENT;
301338a3a47SVladimir Oltean 		goto out;
302338a3a47SVladimir Oltean 	}
3033f6e32f9SVladimir Oltean 
3043f6e32f9SVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount))
305338a3a47SVladimir Oltean 		goto out;
3063f6e32f9SVladimir Oltean 
307c2693363SVladimir Oltean 	err = ds->ops->port_fdb_del(ds, port, addr, vid, db);
3083f6e32f9SVladimir Oltean 	if (err) {
309232deb3fSVladimir Oltean 		refcount_set(&a->refcount, 1);
310338a3a47SVladimir Oltean 		goto out;
3113f6e32f9SVladimir Oltean 	}
3123f6e32f9SVladimir Oltean 
3133f6e32f9SVladimir Oltean 	list_del(&a->list);
3143f6e32f9SVladimir Oltean 	kfree(a);
3153f6e32f9SVladimir Oltean 
316338a3a47SVladimir Oltean out:
317338a3a47SVladimir Oltean 	mutex_unlock(&dp->addr_lists_lock);
318338a3a47SVladimir Oltean 
319338a3a47SVladimir Oltean 	return err;
3203f6e32f9SVladimir Oltean }
3213f6e32f9SVladimir Oltean 
322e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag,
323c2693363SVladimir Oltean 				     const unsigned char *addr, u16 vid,
324c2693363SVladimir Oltean 				     struct dsa_db db)
325e212fa7cSVladimir Oltean {
326e212fa7cSVladimir Oltean 	struct dsa_mac_addr *a;
327e212fa7cSVladimir Oltean 	int err = 0;
328e212fa7cSVladimir Oltean 
329e212fa7cSVladimir Oltean 	mutex_lock(&lag->fdb_lock);
330e212fa7cSVladimir Oltean 
331c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);
332e212fa7cSVladimir Oltean 	if (a) {
333e212fa7cSVladimir Oltean 		refcount_inc(&a->refcount);
334e212fa7cSVladimir Oltean 		goto out;
335e212fa7cSVladimir Oltean 	}
336e212fa7cSVladimir Oltean 
337e212fa7cSVladimir Oltean 	a = kzalloc(sizeof(*a), GFP_KERNEL);
338e212fa7cSVladimir Oltean 	if (!a) {
339e212fa7cSVladimir Oltean 		err = -ENOMEM;
340e212fa7cSVladimir Oltean 		goto out;
341e212fa7cSVladimir Oltean 	}
342e212fa7cSVladimir Oltean 
343c2693363SVladimir Oltean 	err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db);
344e212fa7cSVladimir Oltean 	if (err) {
345e212fa7cSVladimir Oltean 		kfree(a);
346e212fa7cSVladimir Oltean 		goto out;
347e212fa7cSVladimir Oltean 	}
348e212fa7cSVladimir Oltean 
349e212fa7cSVladimir Oltean 	ether_addr_copy(a->addr, addr);
350e212fa7cSVladimir Oltean 	a->vid = vid;
351c7560d12SVladimir Oltean 	a->db = db;
352e212fa7cSVladimir Oltean 	refcount_set(&a->refcount, 1);
353e212fa7cSVladimir Oltean 	list_add_tail(&a->list, &lag->fdbs);
354e212fa7cSVladimir Oltean 
355e212fa7cSVladimir Oltean out:
356e212fa7cSVladimir Oltean 	mutex_unlock(&lag->fdb_lock);
357e212fa7cSVladimir Oltean 
358e212fa7cSVladimir Oltean 	return err;
359e212fa7cSVladimir Oltean }
360e212fa7cSVladimir Oltean 
361e212fa7cSVladimir Oltean static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag,
362c2693363SVladimir Oltean 				     const unsigned char *addr, u16 vid,
363c2693363SVladimir Oltean 				     struct dsa_db db)
364e212fa7cSVladimir Oltean {
365e212fa7cSVladimir Oltean 	struct dsa_mac_addr *a;
366e212fa7cSVladimir Oltean 	int err = 0;
367e212fa7cSVladimir Oltean 
368e212fa7cSVladimir Oltean 	mutex_lock(&lag->fdb_lock);
369e212fa7cSVladimir Oltean 
370c2693363SVladimir Oltean 	a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db);
371e212fa7cSVladimir Oltean 	if (!a) {
372e212fa7cSVladimir Oltean 		err = -ENOENT;
373e212fa7cSVladimir Oltean 		goto out;
374e212fa7cSVladimir Oltean 	}
375e212fa7cSVladimir Oltean 
376e212fa7cSVladimir Oltean 	if (!refcount_dec_and_test(&a->refcount))
377e212fa7cSVladimir Oltean 		goto out;
378e212fa7cSVladimir Oltean 
379c2693363SVladimir Oltean 	err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db);
380e212fa7cSVladimir Oltean 	if (err) {
381e212fa7cSVladimir Oltean 		refcount_set(&a->refcount, 1);
382e212fa7cSVladimir Oltean 		goto out;
383e212fa7cSVladimir Oltean 	}
384e212fa7cSVladimir Oltean 
385e212fa7cSVladimir Oltean 	list_del(&a->list);
386e212fa7cSVladimir Oltean 	kfree(a);
387e212fa7cSVladimir Oltean 
388e212fa7cSVladimir Oltean out:
389e212fa7cSVladimir Oltean 	mutex_unlock(&lag->fdb_lock);
390e212fa7cSVladimir Oltean 
391e212fa7cSVladimir Oltean 	return err;
392e212fa7cSVladimir Oltean }
393e212fa7cSVladimir Oltean 
3943dc80afcSVladimir Oltean static int dsa_switch_host_fdb_add(struct dsa_switch *ds,
3953dc80afcSVladimir Oltean 				   struct dsa_notifier_fdb_info *info)
3963dc80afcSVladimir Oltean {
397fac6abd5SVladimir Oltean 	struct dsa_port *dp;
3983dc80afcSVladimir Oltean 	int err = 0;
3993dc80afcSVladimir Oltean 
4003dc80afcSVladimir Oltean 	if (!ds->ops->port_fdb_add)
4013dc80afcSVladimir Oltean 		return -EOPNOTSUPP;
4023dc80afcSVladimir Oltean 
403fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
404726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
405acc43b7bSVladimir Oltean 			if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) {
406acc43b7bSVladimir Oltean 				err = dsa_switch_do_lag_fdb_add(ds, dp->lag,
407acc43b7bSVladimir Oltean 								info->addr,
408acc43b7bSVladimir Oltean 								info->vid,
409c2693363SVladimir Oltean 								info->db);
410acc43b7bSVladimir Oltean 			} else {
411acc43b7bSVladimir Oltean 				err = dsa_port_do_fdb_add(dp, info->addr,
412acc43b7bSVladimir Oltean 							  info->vid, info->db);
413acc43b7bSVladimir Oltean 			}
4143dc80afcSVladimir Oltean 			if (err)
4153dc80afcSVladimir Oltean 				break;
4163dc80afcSVladimir Oltean 		}
4173dc80afcSVladimir Oltean 	}
4183dc80afcSVladimir Oltean 
4193dc80afcSVladimir Oltean 	return err;
4203dc80afcSVladimir Oltean }
4213dc80afcSVladimir Oltean 
4223dc80afcSVladimir Oltean static int dsa_switch_host_fdb_del(struct dsa_switch *ds,
4233dc80afcSVladimir Oltean 				   struct dsa_notifier_fdb_info *info)
4243dc80afcSVladimir Oltean {
425fac6abd5SVladimir Oltean 	struct dsa_port *dp;
4263f6e32f9SVladimir Oltean 	int err = 0;
4273f6e32f9SVladimir Oltean 
4283dc80afcSVladimir Oltean 	if (!ds->ops->port_fdb_del)
4293dc80afcSVladimir Oltean 		return -EOPNOTSUPP;
4303dc80afcSVladimir Oltean 
431fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
432726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
433acc43b7bSVladimir Oltean 			if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) {
434acc43b7bSVladimir Oltean 				err = dsa_switch_do_lag_fdb_del(ds, dp->lag,
435acc43b7bSVladimir Oltean 								info->addr,
436acc43b7bSVladimir Oltean 								info->vid,
437c2693363SVladimir Oltean 								info->db);
438acc43b7bSVladimir Oltean 			} else {
439acc43b7bSVladimir Oltean 				err = dsa_port_do_fdb_del(dp, info->addr,
440acc43b7bSVladimir Oltean 							  info->vid, info->db);
441acc43b7bSVladimir Oltean 			}
4423f6e32f9SVladimir Oltean 			if (err)
4433f6e32f9SVladimir Oltean 				break;
4443f6e32f9SVladimir Oltean 		}
4453f6e32f9SVladimir Oltean 	}
4463dc80afcSVladimir Oltean 
4473f6e32f9SVladimir Oltean 	return err;
4483dc80afcSVladimir Oltean }
4493dc80afcSVladimir Oltean 
450685fb6a4SVivien Didelot static int dsa_switch_fdb_add(struct dsa_switch *ds,
451685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
452685fb6a4SVivien Didelot {
453726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
454fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
455685fb6a4SVivien Didelot 
4561b6dd556SArkadi Sharshevsky 	if (!ds->ops->port_fdb_add)
457685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
458685fb6a4SVivien Didelot 
459c2693363SVladimir Oltean 	return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db);
460685fb6a4SVivien Didelot }
461685fb6a4SVivien Didelot 
462685fb6a4SVivien Didelot static int dsa_switch_fdb_del(struct dsa_switch *ds,
463685fb6a4SVivien Didelot 			      struct dsa_notifier_fdb_info *info)
464685fb6a4SVivien Didelot {
465726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
466fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
467685fb6a4SVivien Didelot 
468685fb6a4SVivien Didelot 	if (!ds->ops->port_fdb_del)
469685fb6a4SVivien Didelot 		return -EOPNOTSUPP;
470685fb6a4SVivien Didelot 
471c2693363SVladimir Oltean 	return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db);
472685fb6a4SVivien Didelot }
473685fb6a4SVivien Didelot 
474e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_add(struct dsa_switch *ds,
475e212fa7cSVladimir Oltean 				  struct dsa_notifier_lag_fdb_info *info)
476e212fa7cSVladimir Oltean {
477e212fa7cSVladimir Oltean 	struct dsa_port *dp;
478e212fa7cSVladimir Oltean 
479e212fa7cSVladimir Oltean 	if (!ds->ops->lag_fdb_add)
480e212fa7cSVladimir Oltean 		return -EOPNOTSUPP;
481e212fa7cSVladimir Oltean 
482e212fa7cSVladimir Oltean 	/* Notify switch only if it has a port in this LAG */
483e212fa7cSVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
484e212fa7cSVladimir Oltean 		if (dsa_port_offloads_lag(dp, info->lag))
485e212fa7cSVladimir Oltean 			return dsa_switch_do_lag_fdb_add(ds, info->lag,
486c2693363SVladimir Oltean 							 info->addr, info->vid,
487c2693363SVladimir Oltean 							 info->db);
488e212fa7cSVladimir Oltean 
489e212fa7cSVladimir Oltean 	return 0;
490e212fa7cSVladimir Oltean }
491e212fa7cSVladimir Oltean 
492e212fa7cSVladimir Oltean static int dsa_switch_lag_fdb_del(struct dsa_switch *ds,
493e212fa7cSVladimir Oltean 				  struct dsa_notifier_lag_fdb_info *info)
494e212fa7cSVladimir Oltean {
495e212fa7cSVladimir Oltean 	struct dsa_port *dp;
496e212fa7cSVladimir Oltean 
497e212fa7cSVladimir Oltean 	if (!ds->ops->lag_fdb_del)
498e212fa7cSVladimir Oltean 		return -EOPNOTSUPP;
499e212fa7cSVladimir Oltean 
500e212fa7cSVladimir Oltean 	/* Notify switch only if it has a port in this LAG */
501e212fa7cSVladimir Oltean 	dsa_switch_for_each_port(dp, ds)
502e212fa7cSVladimir Oltean 		if (dsa_port_offloads_lag(dp, info->lag))
503e212fa7cSVladimir Oltean 			return dsa_switch_do_lag_fdb_del(ds, info->lag,
504c2693363SVladimir Oltean 							 info->addr, info->vid,
505c2693363SVladimir Oltean 							 info->db);
506e212fa7cSVladimir Oltean 
507e212fa7cSVladimir Oltean 	return 0;
508e212fa7cSVladimir Oltean }
509e212fa7cSVladimir Oltean 
510058102a6STobias Waldekranz static int dsa_switch_lag_change(struct dsa_switch *ds,
511058102a6STobias Waldekranz 				 struct dsa_notifier_lag_info *info)
512058102a6STobias Waldekranz {
513726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_lag_change)
514726816a1SVladimir Oltean 		return ds->ops->port_lag_change(ds, info->dp->index);
515058102a6STobias Waldekranz 
516726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_lag_change)
517726816a1SVladimir Oltean 		return ds->ops->crosschip_lag_change(ds, info->dp->ds->index,
518726816a1SVladimir Oltean 						     info->dp->index);
519058102a6STobias Waldekranz 
520058102a6STobias Waldekranz 	return 0;
521058102a6STobias Waldekranz }
522058102a6STobias Waldekranz 
523058102a6STobias Waldekranz static int dsa_switch_lag_join(struct dsa_switch *ds,
524058102a6STobias Waldekranz 			       struct dsa_notifier_lag_info *info)
525058102a6STobias Waldekranz {
526726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_lag_join)
527726816a1SVladimir Oltean 		return ds->ops->port_lag_join(ds, info->dp->index, info->lag,
5282e359b00SVladimir Oltean 					      info->info, info->extack);
529058102a6STobias Waldekranz 
530726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_lag_join)
531726816a1SVladimir Oltean 		return ds->ops->crosschip_lag_join(ds, info->dp->ds->index,
532726816a1SVladimir Oltean 						   info->dp->index, info->lag,
5332e359b00SVladimir Oltean 						   info->info, info->extack);
534058102a6STobias Waldekranz 
535b71d0987SVladimir Oltean 	return -EOPNOTSUPP;
536058102a6STobias Waldekranz }
537058102a6STobias Waldekranz 
538058102a6STobias Waldekranz static int dsa_switch_lag_leave(struct dsa_switch *ds,
539058102a6STobias Waldekranz 				struct dsa_notifier_lag_info *info)
540058102a6STobias Waldekranz {
541726816a1SVladimir Oltean 	if (info->dp->ds == ds && ds->ops->port_lag_leave)
542726816a1SVladimir Oltean 		return ds->ops->port_lag_leave(ds, info->dp->index, info->lag);
543058102a6STobias Waldekranz 
544726816a1SVladimir Oltean 	if (info->dp->ds != ds && ds->ops->crosschip_lag_leave)
545726816a1SVladimir Oltean 		return ds->ops->crosschip_lag_leave(ds, info->dp->ds->index,
546726816a1SVladimir Oltean 						    info->dp->index, info->lag);
547058102a6STobias Waldekranz 
548b71d0987SVladimir Oltean 	return -EOPNOTSUPP;
549058102a6STobias Waldekranz }
550058102a6STobias Waldekranz 
551ffb68fc5SVladimir Oltean static int dsa_switch_mdb_add(struct dsa_switch *ds,
552e65d45ccSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
553e6db98dbSVivien Didelot {
554726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
555fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
556e6db98dbSVivien Didelot 
557a52b2da7SVladimir Oltean 	if (!ds->ops->port_mdb_add)
558e6db98dbSVivien Didelot 		return -EOPNOTSUPP;
559e6db98dbSVivien Didelot 
560c2693363SVladimir Oltean 	return dsa_port_do_mdb_add(dp, info->mdb, info->db);
561e6db98dbSVivien Didelot }
5628ae5bcdcSVivien Didelot 
5638ae5bcdcSVivien Didelot static int dsa_switch_mdb_del(struct dsa_switch *ds,
5648ae5bcdcSVivien Didelot 			      struct dsa_notifier_mdb_info *info)
5658ae5bcdcSVivien Didelot {
566726816a1SVladimir Oltean 	int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index);
567fac6abd5SVladimir Oltean 	struct dsa_port *dp = dsa_to_port(ds, port);
568161ca59dSVladimir Oltean 
5698ae5bcdcSVivien Didelot 	if (!ds->ops->port_mdb_del)
5708ae5bcdcSVivien Didelot 		return -EOPNOTSUPP;
5718ae5bcdcSVivien Didelot 
572c2693363SVladimir Oltean 	return dsa_port_do_mdb_del(dp, info->mdb, info->db);
5738ae5bcdcSVivien Didelot }
5748ae5bcdcSVivien Didelot 
575b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_add(struct dsa_switch *ds,
576b8e997c4SVladimir Oltean 				   struct dsa_notifier_mdb_info *info)
577b8e997c4SVladimir Oltean {
578fac6abd5SVladimir Oltean 	struct dsa_port *dp;
579b8e997c4SVladimir Oltean 	int err = 0;
580b8e997c4SVladimir Oltean 
581b8e997c4SVladimir Oltean 	if (!ds->ops->port_mdb_add)
582b8e997c4SVladimir Oltean 		return -EOPNOTSUPP;
583b8e997c4SVladimir Oltean 
584fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
585726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
586c2693363SVladimir Oltean 			err = dsa_port_do_mdb_add(dp, info->mdb, info->db);
587b8e997c4SVladimir Oltean 			if (err)
588b8e997c4SVladimir Oltean 				break;
589b8e997c4SVladimir Oltean 		}
590b8e997c4SVladimir Oltean 	}
591b8e997c4SVladimir Oltean 
592b8e997c4SVladimir Oltean 	return err;
593b8e997c4SVladimir Oltean }
594b8e997c4SVladimir Oltean 
595b8e997c4SVladimir Oltean static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
596b8e997c4SVladimir Oltean 				   struct dsa_notifier_mdb_info *info)
597b8e997c4SVladimir Oltean {
598fac6abd5SVladimir Oltean 	struct dsa_port *dp;
599161ca59dSVladimir Oltean 	int err = 0;
600161ca59dSVladimir Oltean 
601b8e997c4SVladimir Oltean 	if (!ds->ops->port_mdb_del)
602b8e997c4SVladimir Oltean 		return -EOPNOTSUPP;
603b8e997c4SVladimir Oltean 
604fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
605726816a1SVladimir Oltean 		if (dsa_port_host_address_match(dp, info->dp)) {
606c2693363SVladimir Oltean 			err = dsa_port_do_mdb_del(dp, info->mdb, info->db);
607161ca59dSVladimir Oltean 			if (err)
608161ca59dSVladimir Oltean 				break;
609161ca59dSVladimir Oltean 		}
610161ca59dSVladimir Oltean 	}
611b8e997c4SVladimir Oltean 
612161ca59dSVladimir Oltean 	return err;
613b8e997c4SVladimir Oltean }
614b8e997c4SVladimir Oltean 
615134ef238SVladimir Oltean /* Port VLANs match on the targeted port and on all DSA ports */
616fac6abd5SVladimir Oltean static bool dsa_port_vlan_match(struct dsa_port *dp,
617e65d45ccSVivien Didelot 				struct dsa_notifier_vlan_info *info)
618e65d45ccSVivien Didelot {
619726816a1SVladimir Oltean 	return dsa_port_is_dsa(dp) || dp == info->dp;
620e65d45ccSVivien Didelot }
621e65d45ccSVivien Didelot 
622134ef238SVladimir Oltean /* Host VLANs match on the targeted port's CPU port, and on all DSA ports
623134ef238SVladimir Oltean  * (upstream and downstream) of that switch and its upstream switches.
624134ef238SVladimir Oltean  */
625134ef238SVladimir Oltean static bool dsa_port_host_vlan_match(struct dsa_port *dp,
626726816a1SVladimir Oltean 				     const struct dsa_port *targeted_dp)
627134ef238SVladimir Oltean {
628726816a1SVladimir Oltean 	struct dsa_port *cpu_dp = targeted_dp->cpu_dp;
629134ef238SVladimir Oltean 
630726816a1SVladimir Oltean 	if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds))
631134ef238SVladimir Oltean 		return dsa_port_is_dsa(dp) || dp == cpu_dp;
632134ef238SVladimir Oltean 
633134ef238SVladimir Oltean 	return false;
634134ef238SVladimir Oltean }
635134ef238SVladimir Oltean 
636134ef238SVladimir Oltean static struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list,
637134ef238SVladimir Oltean 				      const struct switchdev_obj_port_vlan *vlan)
638134ef238SVladimir Oltean {
639134ef238SVladimir Oltean 	struct dsa_vlan *v;
640134ef238SVladimir Oltean 
641134ef238SVladimir Oltean 	list_for_each_entry(v, vlan_list, list)
642134ef238SVladimir Oltean 		if (v->vid == vlan->vid)
643134ef238SVladimir Oltean 			return v;
644134ef238SVladimir Oltean 
645134ef238SVladimir Oltean 	return NULL;
646134ef238SVladimir Oltean }
647134ef238SVladimir Oltean 
648134ef238SVladimir Oltean static int dsa_port_do_vlan_add(struct dsa_port *dp,
649134ef238SVladimir Oltean 				const struct switchdev_obj_port_vlan *vlan,
650134ef238SVladimir Oltean 				struct netlink_ext_ack *extack)
651134ef238SVladimir Oltean {
652134ef238SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
653134ef238SVladimir Oltean 	int port = dp->index;
654134ef238SVladimir Oltean 	struct dsa_vlan *v;
655134ef238SVladimir Oltean 	int err = 0;
656134ef238SVladimir Oltean 
657134ef238SVladimir Oltean 	/* No need to bother with refcounting for user ports. */
658134ef238SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
659134ef238SVladimir Oltean 		return ds->ops->port_vlan_add(ds, port, vlan, extack);
660134ef238SVladimir Oltean 
661134ef238SVladimir Oltean 	/* No need to propagate on shared ports the existing VLANs that were
662134ef238SVladimir Oltean 	 * re-notified after just the flags have changed. This would cause a
663134ef238SVladimir Oltean 	 * refcount bump which we need to avoid, since it unbalances the
664134ef238SVladimir Oltean 	 * additions with the deletions.
665134ef238SVladimir Oltean 	 */
666134ef238SVladimir Oltean 	if (vlan->changed)
667134ef238SVladimir Oltean 		return 0;
668134ef238SVladimir Oltean 
669134ef238SVladimir Oltean 	mutex_lock(&dp->vlans_lock);
670134ef238SVladimir Oltean 
671134ef238SVladimir Oltean 	v = dsa_vlan_find(&dp->vlans, vlan);
672134ef238SVladimir Oltean 	if (v) {
673134ef238SVladimir Oltean 		refcount_inc(&v->refcount);
674134ef238SVladimir Oltean 		goto out;
675134ef238SVladimir Oltean 	}
676134ef238SVladimir Oltean 
677134ef238SVladimir Oltean 	v = kzalloc(sizeof(*v), GFP_KERNEL);
678134ef238SVladimir Oltean 	if (!v) {
679134ef238SVladimir Oltean 		err = -ENOMEM;
680134ef238SVladimir Oltean 		goto out;
681134ef238SVladimir Oltean 	}
682134ef238SVladimir Oltean 
683134ef238SVladimir Oltean 	err = ds->ops->port_vlan_add(ds, port, vlan, extack);
684134ef238SVladimir Oltean 	if (err) {
685134ef238SVladimir Oltean 		kfree(v);
686134ef238SVladimir Oltean 		goto out;
687134ef238SVladimir Oltean 	}
688134ef238SVladimir Oltean 
689134ef238SVladimir Oltean 	v->vid = vlan->vid;
690134ef238SVladimir Oltean 	refcount_set(&v->refcount, 1);
691134ef238SVladimir Oltean 	list_add_tail(&v->list, &dp->vlans);
692134ef238SVladimir Oltean 
693134ef238SVladimir Oltean out:
694134ef238SVladimir Oltean 	mutex_unlock(&dp->vlans_lock);
695134ef238SVladimir Oltean 
696134ef238SVladimir Oltean 	return err;
697134ef238SVladimir Oltean }
698134ef238SVladimir Oltean 
699134ef238SVladimir Oltean static int dsa_port_do_vlan_del(struct dsa_port *dp,
700134ef238SVladimir Oltean 				const struct switchdev_obj_port_vlan *vlan)
701134ef238SVladimir Oltean {
702134ef238SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
703134ef238SVladimir Oltean 	int port = dp->index;
704134ef238SVladimir Oltean 	struct dsa_vlan *v;
705134ef238SVladimir Oltean 	int err = 0;
706134ef238SVladimir Oltean 
707134ef238SVladimir Oltean 	/* No need to bother with refcounting for user ports */
708134ef238SVladimir Oltean 	if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
709134ef238SVladimir Oltean 		return ds->ops->port_vlan_del(ds, port, vlan);
710134ef238SVladimir Oltean 
711134ef238SVladimir Oltean 	mutex_lock(&dp->vlans_lock);
712134ef238SVladimir Oltean 
713134ef238SVladimir Oltean 	v = dsa_vlan_find(&dp->vlans, vlan);
714134ef238SVladimir Oltean 	if (!v) {
715134ef238SVladimir Oltean 		err = -ENOENT;
716134ef238SVladimir Oltean 		goto out;
717134ef238SVladimir Oltean 	}
718134ef238SVladimir Oltean 
719134ef238SVladimir Oltean 	if (!refcount_dec_and_test(&v->refcount))
720134ef238SVladimir Oltean 		goto out;
721134ef238SVladimir Oltean 
722134ef238SVladimir Oltean 	err = ds->ops->port_vlan_del(ds, port, vlan);
723134ef238SVladimir Oltean 	if (err) {
724134ef238SVladimir Oltean 		refcount_set(&v->refcount, 1);
725134ef238SVladimir Oltean 		goto out;
726134ef238SVladimir Oltean 	}
727134ef238SVladimir Oltean 
728134ef238SVladimir Oltean 	list_del(&v->list);
729134ef238SVladimir Oltean 	kfree(v);
730134ef238SVladimir Oltean 
731134ef238SVladimir Oltean out:
732134ef238SVladimir Oltean 	mutex_unlock(&dp->vlans_lock);
733134ef238SVladimir Oltean 
734134ef238SVladimir Oltean 	return err;
735134ef238SVladimir Oltean }
736134ef238SVladimir Oltean 
737ffb68fc5SVladimir Oltean static int dsa_switch_vlan_add(struct dsa_switch *ds,
738e65d45ccSVivien Didelot 			       struct dsa_notifier_vlan_info *info)
7399c428c59SVivien Didelot {
740fac6abd5SVladimir Oltean 	struct dsa_port *dp;
741fac6abd5SVladimir Oltean 	int err;
7429c428c59SVivien Didelot 
7431958d581SVladimir Oltean 	if (!ds->ops->port_vlan_add)
7449c428c59SVivien Didelot 		return -EOPNOTSUPP;
7459c428c59SVivien Didelot 
746fac6abd5SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
747fac6abd5SVladimir Oltean 		if (dsa_port_vlan_match(dp, info)) {
748134ef238SVladimir Oltean 			err = dsa_port_do_vlan_add(dp, info->vlan,
74931046a5fSVladimir Oltean 						   info->extack);
7509c428c59SVivien Didelot 			if (err)
7519c428c59SVivien Didelot 				return err;
7529c428c59SVivien Didelot 		}
753e65d45ccSVivien Didelot 	}
7549c428c59SVivien Didelot 
755d0c627b8SVivien Didelot 	return 0;
756d0c627b8SVivien Didelot }
757d0c627b8SVivien Didelot 
758d0c627b8SVivien Didelot static int dsa_switch_vlan_del(struct dsa_switch *ds,
759d0c627b8SVivien Didelot 			       struct dsa_notifier_vlan_info *info)
760d0c627b8SVivien Didelot {
761134ef238SVladimir Oltean 	struct dsa_port *dp;
762134ef238SVladimir Oltean 	int err;
763134ef238SVladimir Oltean 
764d0c627b8SVivien Didelot 	if (!ds->ops->port_vlan_del)
765d0c627b8SVivien Didelot 		return -EOPNOTSUPP;
766d0c627b8SVivien Didelot 
767134ef238SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
768134ef238SVladimir Oltean 		if (dsa_port_vlan_match(dp, info)) {
769134ef238SVladimir Oltean 			err = dsa_port_do_vlan_del(dp, info->vlan);
770134ef238SVladimir Oltean 			if (err)
771134ef238SVladimir Oltean 				return err;
772134ef238SVladimir Oltean 		}
773134ef238SVladimir Oltean 	}
7741ca4aa9cSVivien Didelot 
775134ef238SVladimir Oltean 	return 0;
776134ef238SVladimir Oltean }
777134ef238SVladimir Oltean 
778134ef238SVladimir Oltean static int dsa_switch_host_vlan_add(struct dsa_switch *ds,
779134ef238SVladimir Oltean 				    struct dsa_notifier_vlan_info *info)
780134ef238SVladimir Oltean {
781134ef238SVladimir Oltean 	struct dsa_port *dp;
782134ef238SVladimir Oltean 	int err;
783134ef238SVladimir Oltean 
784134ef238SVladimir Oltean 	if (!ds->ops->port_vlan_add)
785134ef238SVladimir Oltean 		return -EOPNOTSUPP;
786134ef238SVladimir Oltean 
787134ef238SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
788726816a1SVladimir Oltean 		if (dsa_port_host_vlan_match(dp, info->dp)) {
789134ef238SVladimir Oltean 			err = dsa_port_do_vlan_add(dp, info->vlan,
790134ef238SVladimir Oltean 						   info->extack);
791134ef238SVladimir Oltean 			if (err)
792134ef238SVladimir Oltean 				return err;
793134ef238SVladimir Oltean 		}
794134ef238SVladimir Oltean 	}
795134ef238SVladimir Oltean 
796134ef238SVladimir Oltean 	return 0;
797134ef238SVladimir Oltean }
798134ef238SVladimir Oltean 
799134ef238SVladimir Oltean static int dsa_switch_host_vlan_del(struct dsa_switch *ds,
800134ef238SVladimir Oltean 				    struct dsa_notifier_vlan_info *info)
801134ef238SVladimir Oltean {
802134ef238SVladimir Oltean 	struct dsa_port *dp;
803134ef238SVladimir Oltean 	int err;
804134ef238SVladimir Oltean 
805134ef238SVladimir Oltean 	if (!ds->ops->port_vlan_del)
806134ef238SVladimir Oltean 		return -EOPNOTSUPP;
807134ef238SVladimir Oltean 
808134ef238SVladimir Oltean 	dsa_switch_for_each_port(dp, ds) {
809726816a1SVladimir Oltean 		if (dsa_port_host_vlan_match(dp, info->dp)) {
810134ef238SVladimir Oltean 			err = dsa_port_do_vlan_del(dp, info->vlan);
811134ef238SVladimir Oltean 			if (err)
812134ef238SVladimir Oltean 				return err;
813134ef238SVladimir Oltean 		}
814134ef238SVladimir Oltean 	}
815134ef238SVladimir Oltean 
8161ca4aa9cSVivien Didelot 	return 0;
817d0c627b8SVivien Didelot }
818d0c627b8SVivien Didelot 
81953da0ebaSVladimir Oltean static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
82053da0ebaSVladimir Oltean 				       struct dsa_notifier_tag_proto_info *info)
82153da0ebaSVladimir Oltean {
82253da0ebaSVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
823d0004a02SVladimir Oltean 	struct dsa_port *dp, *cpu_dp;
824d0004a02SVladimir Oltean 	int err;
82553da0ebaSVladimir Oltean 
82653da0ebaSVladimir Oltean 	if (!ds->ops->change_tag_protocol)
82753da0ebaSVladimir Oltean 		return -EOPNOTSUPP;
82853da0ebaSVladimir Oltean 
82953da0ebaSVladimir Oltean 	ASSERT_RTNL();
83053da0ebaSVladimir Oltean 
831bacf93b0SVladimir Oltean 	err = ds->ops->change_tag_protocol(ds, tag_ops->proto);
83253da0ebaSVladimir Oltean 	if (err)
83353da0ebaSVladimir Oltean 		return err;
83453da0ebaSVladimir Oltean 
835bacf93b0SVladimir Oltean 	dsa_switch_for_each_cpu_port(cpu_dp, ds)
836d0004a02SVladimir Oltean 		dsa_port_set_tag_protocol(cpu_dp, tag_ops);
83753da0ebaSVladimir Oltean 
83853da0ebaSVladimir Oltean 	/* Now that changing the tag protocol can no longer fail, let's update
83953da0ebaSVladimir Oltean 	 * the remaining bits which are "duplicated for faster access", and the
84053da0ebaSVladimir Oltean 	 * bits that depend on the tagger, such as the MTU.
84153da0ebaSVladimir Oltean 	 */
842d0004a02SVladimir Oltean 	dsa_switch_for_each_user_port(dp, ds) {
843d0004a02SVladimir Oltean 		struct net_device *slave = dp->slave;
84453da0ebaSVladimir Oltean 
84553da0ebaSVladimir Oltean 		dsa_slave_setup_tagger(slave);
84653da0ebaSVladimir Oltean 
84753da0ebaSVladimir Oltean 		/* rtnl_mutex is held in dsa_tree_change_tag_proto */
84853da0ebaSVladimir Oltean 		dsa_slave_change_mtu(slave, slave->mtu);
84953da0ebaSVladimir Oltean 	}
85053da0ebaSVladimir Oltean 
85153da0ebaSVladimir Oltean 	return 0;
85253da0ebaSVladimir Oltean }
85353da0ebaSVladimir Oltean 
8547f297314SVladimir Oltean /* We use the same cross-chip notifiers to inform both the tagger side, as well
8557f297314SVladimir Oltean  * as the switch side, of connection and disconnection events.
8567f297314SVladimir Oltean  * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the
8577f297314SVladimir Oltean  * switch side doesn't support connecting to this tagger, and therefore, the
8587f297314SVladimir Oltean  * fact that we don't disconnect the tagger side doesn't constitute a memory
8597f297314SVladimir Oltean  * leak: the tagger will still operate with persistent per-switch memory, just
8607f297314SVladimir Oltean  * with the switch side unconnected to it. What does constitute a hard error is
8617f297314SVladimir Oltean  * when the switch side supports connecting but fails.
8627f297314SVladimir Oltean  */
8637f297314SVladimir Oltean static int
8647f297314SVladimir Oltean dsa_switch_connect_tag_proto(struct dsa_switch *ds,
865dc452a47SVladimir Oltean 			     struct dsa_notifier_tag_proto_info *info)
866dc452a47SVladimir Oltean {
867dc452a47SVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
8687f297314SVladimir Oltean 	int err;
8697f297314SVladimir Oltean 
8707f297314SVladimir Oltean 	/* Notify the new tagger about the connection to this switch */
8717f297314SVladimir Oltean 	if (tag_ops->connect) {
8727f297314SVladimir Oltean 		err = tag_ops->connect(ds);
8737f297314SVladimir Oltean 		if (err)
8747f297314SVladimir Oltean 			return err;
8757f297314SVladimir Oltean 	}
876dc452a47SVladimir Oltean 
877dc452a47SVladimir Oltean 	if (!ds->ops->connect_tag_protocol)
878dc452a47SVladimir Oltean 		return -EOPNOTSUPP;
879dc452a47SVladimir Oltean 
8807f297314SVladimir Oltean 	/* Notify the switch about the connection to the new tagger */
8817f297314SVladimir Oltean 	err = ds->ops->connect_tag_protocol(ds, tag_ops->proto);
8827f297314SVladimir Oltean 	if (err) {
8837f297314SVladimir Oltean 		/* Revert the new tagger's connection to this tree */
8847f297314SVladimir Oltean 		if (tag_ops->disconnect)
8857f297314SVladimir Oltean 			tag_ops->disconnect(ds);
8867f297314SVladimir Oltean 		return err;
8877f297314SVladimir Oltean 	}
8887f297314SVladimir Oltean 
8897f297314SVladimir Oltean 	return 0;
8907f297314SVladimir Oltean }
8917f297314SVladimir Oltean 
8927f297314SVladimir Oltean static int
8937f297314SVladimir Oltean dsa_switch_disconnect_tag_proto(struct dsa_switch *ds,
8947f297314SVladimir Oltean 				struct dsa_notifier_tag_proto_info *info)
8957f297314SVladimir Oltean {
8967f297314SVladimir Oltean 	const struct dsa_device_ops *tag_ops = info->tag_ops;
8977f297314SVladimir Oltean 
8987f297314SVladimir Oltean 	/* Notify the tagger about the disconnection from this switch */
8997f297314SVladimir Oltean 	if (tag_ops->disconnect && ds->tagger_data)
9007f297314SVladimir Oltean 		tag_ops->disconnect(ds);
9017f297314SVladimir Oltean 
9027f297314SVladimir Oltean 	/* No need to notify the switch, since it shouldn't have any
9037f297314SVladimir Oltean 	 * resources to tear down
9047f297314SVladimir Oltean 	 */
9057f297314SVladimir Oltean 	return 0;
906dc452a47SVladimir Oltean }
907dc452a47SVladimir Oltean 
908295ab96fSVladimir Oltean static int
909295ab96fSVladimir Oltean dsa_switch_master_state_change(struct dsa_switch *ds,
910295ab96fSVladimir Oltean 			       struct dsa_notifier_master_state_info *info)
911295ab96fSVladimir Oltean {
912295ab96fSVladimir Oltean 	if (!ds->ops->master_state_change)
913295ab96fSVladimir Oltean 		return 0;
914295ab96fSVladimir Oltean 
915295ab96fSVladimir Oltean 	ds->ops->master_state_change(ds, info->master, info->operational);
916295ab96fSVladimir Oltean 
917295ab96fSVladimir Oltean 	return 0;
918295ab96fSVladimir Oltean }
919295ab96fSVladimir Oltean 
920f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb,
921f515f192SVivien Didelot 			    unsigned long event, void *info)
922f515f192SVivien Didelot {
923f515f192SVivien Didelot 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
924f515f192SVivien Didelot 	int err;
925f515f192SVivien Didelot 
926f515f192SVivien Didelot 	switch (event) {
9271faabf74SVivien Didelot 	case DSA_NOTIFIER_AGEING_TIME:
9281faabf74SVivien Didelot 		err = dsa_switch_ageing_time(ds, info);
9291faabf74SVivien Didelot 		break;
93004d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_JOIN:
93104d3a4c6SVivien Didelot 		err = dsa_switch_bridge_join(ds, info);
93204d3a4c6SVivien Didelot 		break;
93304d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_LEAVE:
93404d3a4c6SVivien Didelot 		err = dsa_switch_bridge_leave(ds, info);
93504d3a4c6SVivien Didelot 		break;
936685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_ADD:
937685fb6a4SVivien Didelot 		err = dsa_switch_fdb_add(ds, info);
938685fb6a4SVivien Didelot 		break;
939685fb6a4SVivien Didelot 	case DSA_NOTIFIER_FDB_DEL:
940685fb6a4SVivien Didelot 		err = dsa_switch_fdb_del(ds, info);
941685fb6a4SVivien Didelot 		break;
9423dc80afcSVladimir Oltean 	case DSA_NOTIFIER_HOST_FDB_ADD:
9433dc80afcSVladimir Oltean 		err = dsa_switch_host_fdb_add(ds, info);
9443dc80afcSVladimir Oltean 		break;
9453dc80afcSVladimir Oltean 	case DSA_NOTIFIER_HOST_FDB_DEL:
9463dc80afcSVladimir Oltean 		err = dsa_switch_host_fdb_del(ds, info);
9473dc80afcSVladimir Oltean 		break;
948e212fa7cSVladimir Oltean 	case DSA_NOTIFIER_LAG_FDB_ADD:
949e212fa7cSVladimir Oltean 		err = dsa_switch_lag_fdb_add(ds, info);
950e212fa7cSVladimir Oltean 		break;
951e212fa7cSVladimir Oltean 	case DSA_NOTIFIER_LAG_FDB_DEL:
952e212fa7cSVladimir Oltean 		err = dsa_switch_lag_fdb_del(ds, info);
953e212fa7cSVladimir Oltean 		break;
954058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_CHANGE:
955058102a6STobias Waldekranz 		err = dsa_switch_lag_change(ds, info);
956058102a6STobias Waldekranz 		break;
957058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_JOIN:
958058102a6STobias Waldekranz 		err = dsa_switch_lag_join(ds, info);
959058102a6STobias Waldekranz 		break;
960058102a6STobias Waldekranz 	case DSA_NOTIFIER_LAG_LEAVE:
961058102a6STobias Waldekranz 		err = dsa_switch_lag_leave(ds, info);
962058102a6STobias Waldekranz 		break;
9638ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_ADD:
9648ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_add(ds, info);
9658ae5bcdcSVivien Didelot 		break;
9668ae5bcdcSVivien Didelot 	case DSA_NOTIFIER_MDB_DEL:
9678ae5bcdcSVivien Didelot 		err = dsa_switch_mdb_del(ds, info);
9688ae5bcdcSVivien Didelot 		break;
969b8e997c4SVladimir Oltean 	case DSA_NOTIFIER_HOST_MDB_ADD:
970b8e997c4SVladimir Oltean 		err = dsa_switch_host_mdb_add(ds, info);
971b8e997c4SVladimir Oltean 		break;
972b8e997c4SVladimir Oltean 	case DSA_NOTIFIER_HOST_MDB_DEL:
973b8e997c4SVladimir Oltean 		err = dsa_switch_host_mdb_del(ds, info);
974b8e997c4SVladimir Oltean 		break;
975d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_ADD:
976d0c627b8SVivien Didelot 		err = dsa_switch_vlan_add(ds, info);
977d0c627b8SVivien Didelot 		break;
978d0c627b8SVivien Didelot 	case DSA_NOTIFIER_VLAN_DEL:
979d0c627b8SVivien Didelot 		err = dsa_switch_vlan_del(ds, info);
980d0c627b8SVivien Didelot 		break;
981134ef238SVladimir Oltean 	case DSA_NOTIFIER_HOST_VLAN_ADD:
982134ef238SVladimir Oltean 		err = dsa_switch_host_vlan_add(ds, info);
983134ef238SVladimir Oltean 		break;
984134ef238SVladimir Oltean 	case DSA_NOTIFIER_HOST_VLAN_DEL:
985134ef238SVladimir Oltean 		err = dsa_switch_host_vlan_del(ds, info);
986134ef238SVladimir Oltean 		break;
987bfcb8132SVladimir Oltean 	case DSA_NOTIFIER_MTU:
988bfcb8132SVladimir Oltean 		err = dsa_switch_mtu(ds, info);
989bfcb8132SVladimir Oltean 		break;
99053da0ebaSVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO:
99153da0ebaSVladimir Oltean 		err = dsa_switch_change_tag_proto(ds, info);
99253da0ebaSVladimir Oltean 		break;
993dc452a47SVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO_CONNECT:
994dc452a47SVladimir Oltean 		err = dsa_switch_connect_tag_proto(ds, info);
995dc452a47SVladimir Oltean 		break;
9967f297314SVladimir Oltean 	case DSA_NOTIFIER_TAG_PROTO_DISCONNECT:
9977f297314SVladimir Oltean 		err = dsa_switch_disconnect_tag_proto(ds, info);
9987f297314SVladimir Oltean 		break;
999c64b9c05SVladimir Oltean 	case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD:
1000c64b9c05SVladimir Oltean 		err = dsa_switch_tag_8021q_vlan_add(ds, info);
1001c64b9c05SVladimir Oltean 		break;
1002c64b9c05SVladimir Oltean 	case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
1003c64b9c05SVladimir Oltean 		err = dsa_switch_tag_8021q_vlan_del(ds, info);
1004c64b9c05SVladimir Oltean 		break;
1005295ab96fSVladimir Oltean 	case DSA_NOTIFIER_MASTER_STATE_CHANGE:
1006295ab96fSVladimir Oltean 		err = dsa_switch_master_state_change(ds, info);
1007295ab96fSVladimir Oltean 		break;
1008f515f192SVivien Didelot 	default:
1009f515f192SVivien Didelot 		err = -EOPNOTSUPP;
1010f515f192SVivien Didelot 		break;
1011f515f192SVivien Didelot 	}
1012f515f192SVivien Didelot 
1013f515f192SVivien Didelot 	if (err)
1014f515f192SVivien Didelot 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
1015f515f192SVivien Didelot 			event, err);
1016f515f192SVivien Didelot 
1017f515f192SVivien Didelot 	return notifier_from_errno(err);
1018f515f192SVivien Didelot }
1019f515f192SVivien Didelot 
10206dbdfce7SVladimir Oltean /**
10216dbdfce7SVladimir Oltean  * dsa_tree_notify - Execute code for all switches in a DSA switch tree.
10226dbdfce7SVladimir Oltean  * @dst: collection of struct dsa_switch devices to notify.
10236dbdfce7SVladimir Oltean  * @e: event, must be of type DSA_NOTIFIER_*
10246dbdfce7SVladimir Oltean  * @v: event-specific value.
10256dbdfce7SVladimir Oltean  *
10266dbdfce7SVladimir Oltean  * Given a struct dsa_switch_tree, this can be used to run a function once for
10276dbdfce7SVladimir Oltean  * each member DSA switch. The other alternative of traversing the tree is only
10286dbdfce7SVladimir Oltean  * through its ports list, which does not uniquely list the switches.
10296dbdfce7SVladimir Oltean  */
10306dbdfce7SVladimir Oltean int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v)
10316dbdfce7SVladimir Oltean {
10326dbdfce7SVladimir Oltean 	struct raw_notifier_head *nh = &dst->nh;
10336dbdfce7SVladimir Oltean 	int err;
10346dbdfce7SVladimir Oltean 
10356dbdfce7SVladimir Oltean 	err = raw_notifier_call_chain(nh, e, v);
10366dbdfce7SVladimir Oltean 
10376dbdfce7SVladimir Oltean 	return notifier_to_errno(err);
10386dbdfce7SVladimir Oltean }
10396dbdfce7SVladimir Oltean 
10406dbdfce7SVladimir Oltean /**
10416dbdfce7SVladimir Oltean  * dsa_broadcast - Notify all DSA trees in the system.
10426dbdfce7SVladimir Oltean  * @e: event, must be of type DSA_NOTIFIER_*
10436dbdfce7SVladimir Oltean  * @v: event-specific value.
10446dbdfce7SVladimir Oltean  *
10456dbdfce7SVladimir Oltean  * Can be used to notify the switching fabric of events such as cross-chip
10466dbdfce7SVladimir Oltean  * bridging between disjoint trees (such as islands of tagger-compatible
10476dbdfce7SVladimir Oltean  * switches bridged by an incompatible middle switch).
10486dbdfce7SVladimir Oltean  *
10496dbdfce7SVladimir Oltean  * WARNING: this function is not reliable during probe time, because probing
10506dbdfce7SVladimir Oltean  * between trees is asynchronous and not all DSA trees might have probed.
10516dbdfce7SVladimir Oltean  */
10526dbdfce7SVladimir Oltean int dsa_broadcast(unsigned long e, void *v)
10536dbdfce7SVladimir Oltean {
10546dbdfce7SVladimir Oltean 	struct dsa_switch_tree *dst;
10556dbdfce7SVladimir Oltean 	int err = 0;
10566dbdfce7SVladimir Oltean 
10576dbdfce7SVladimir Oltean 	list_for_each_entry(dst, &dsa_tree_list, list) {
10586dbdfce7SVladimir Oltean 		err = dsa_tree_notify(dst, e, v);
10596dbdfce7SVladimir Oltean 		if (err)
10606dbdfce7SVladimir Oltean 			break;
10616dbdfce7SVladimir Oltean 	}
10626dbdfce7SVladimir Oltean 
10636dbdfce7SVladimir Oltean 	return err;
10646dbdfce7SVladimir Oltean }
10656dbdfce7SVladimir Oltean 
1066f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds)
1067f515f192SVivien Didelot {
1068f515f192SVivien Didelot 	ds->nb.notifier_call = dsa_switch_event;
1069f515f192SVivien Didelot 
1070f515f192SVivien Didelot 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
1071f515f192SVivien Didelot }
1072f515f192SVivien Didelot 
1073f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds)
1074f515f192SVivien Didelot {
1075f515f192SVivien Didelot 	int err;
1076f515f192SVivien Didelot 
1077f515f192SVivien Didelot 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
1078f515f192SVivien Didelot 	if (err)
1079f515f192SVivien Didelot 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
1080f515f192SVivien Didelot }
1081