Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
91da11f8 | 2 | /* |
6ca80638 | 3 | * net/dsa/user.c - user device handling |
e84665c9 | 4 | * Copyright (c) 2008-2009 Marvell Semiconductor |
91da11f8 LB |
5 | */ |
6 | ||
7 | #include <linux/list.h> | |
df02c6ff | 8 | #include <linux/etherdevice.h> |
b73adef6 | 9 | #include <linux/netdevice.h> |
91da11f8 | 10 | #include <linux/phy.h> |
a2820543 | 11 | #include <linux/phy_fixed.h> |
aab9c406 | 12 | #include <linux/phylink.h> |
0d8bcdd3 FF |
13 | #include <linux/of_net.h> |
14 | #include <linux/of_mdio.h> | |
7f854420 | 15 | #include <linux/mdio.h> |
b73adef6 | 16 | #include <net/rtnetlink.h> |
f50f2127 | 17 | #include <net/pkt_cls.h> |
a71acad9 | 18 | #include <net/selftests.h> |
f50f2127 | 19 | #include <net/tc_act/tc_mirred.h> |
b73adef6 | 20 | #include <linux/if_bridge.h> |
18596f50 | 21 | #include <linux/if_hsr.h> |
d538eca8 | 22 | #include <net/dcbnl.h> |
04ff53f9 | 23 | #include <linux/netpoll.h> |
5c9f7b04 | 24 | #include <linux/string.h> |
ea5dd34b | 25 | |
6ca80638 | 26 | #include "conduit.h" |
47d2ce03 | 27 | #include "dsa.h" |
5917bfe6 | 28 | #include "netlink.h" |
6ca80638 | 29 | #include "port.h" |
d06f925f | 30 | #include "switch.h" |
bd954b82 | 31 | #include "tag.h" |
6ca80638 | 32 | #include "user.h" |
91da11f8 | 33 | |
8e396fec VO |
34 | struct dsa_switchdev_event_work { |
35 | struct net_device *dev; | |
36 | struct net_device *orig_dev; | |
37 | struct work_struct work; | |
38 | unsigned long event; | |
39 | /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and | |
40 | * SWITCHDEV_FDB_DEL_TO_DEVICE | |
41 | */ | |
42 | unsigned char addr[ETH_ALEN]; | |
43 | u16 vid; | |
44 | bool host_addr; | |
45 | }; | |
46 | ||
47 | enum dsa_standalone_event { | |
48 | DSA_UC_ADD, | |
49 | DSA_UC_DEL, | |
50 | DSA_MC_ADD, | |
51 | DSA_MC_DEL, | |
52 | }; | |
53 | ||
54 | struct dsa_standalone_event_work { | |
55 | struct work_struct work; | |
56 | struct net_device *dev; | |
57 | enum dsa_standalone_event event; | |
58 | unsigned char addr[ETH_ALEN]; | |
59 | u16 vid; | |
60 | }; | |
61 | ||
64fdc5f3 VO |
62 | struct dsa_host_vlan_rx_filtering_ctx { |
63 | struct net_device *dev; | |
64 | const unsigned char *addr; | |
65 | enum dsa_standalone_event event; | |
66 | }; | |
67 | ||
8e396fec VO |
68 | static bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds) |
69 | { | |
70 | return ds->ops->port_fdb_add && ds->ops->port_fdb_del && | |
71 | ds->fdb_isolation && !ds->vlan_filtering_is_global && | |
72 | !ds->needs_standalone_vlan_filtering; | |
73 | } | |
74 | ||
75 | static bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds) | |
76 | { | |
77 | return ds->ops->port_mdb_add && ds->ops->port_mdb_del && | |
78 | ds->fdb_isolation && !ds->vlan_filtering_is_global && | |
79 | !ds->needs_standalone_vlan_filtering; | |
80 | } | |
81 | ||
6ca80638 | 82 | static void dsa_user_standalone_event_work(struct work_struct *work) |
5e8a1e03 VO |
83 | { |
84 | struct dsa_standalone_event_work *standalone_work = | |
85 | container_of(work, struct dsa_standalone_event_work, work); | |
86 | const unsigned char *addr = standalone_work->addr; | |
87 | struct net_device *dev = standalone_work->dev; | |
6ca80638 | 88 | struct dsa_port *dp = dsa_user_to_port(dev); |
5e8a1e03 VO |
89 | struct switchdev_obj_port_mdb mdb; |
90 | struct dsa_switch *ds = dp->ds; | |
91 | u16 vid = standalone_work->vid; | |
92 | int err; | |
93 | ||
94 | switch (standalone_work->event) { | |
95 | case DSA_UC_ADD: | |
96 | err = dsa_port_standalone_host_fdb_add(dp, addr, vid); | |
97 | if (err) { | |
98 | dev_err(ds->dev, | |
99 | "port %d failed to add %pM vid %d to fdb: %d\n", | |
100 | dp->index, addr, vid, err); | |
101 | break; | |
102 | } | |
103 | break; | |
104 | ||
105 | case DSA_UC_DEL: | |
106 | err = dsa_port_standalone_host_fdb_del(dp, addr, vid); | |
107 | if (err) { | |
108 | dev_err(ds->dev, | |
109 | "port %d failed to delete %pM vid %d from fdb: %d\n", | |
110 | dp->index, addr, vid, err); | |
111 | } | |
112 | ||
113 | break; | |
114 | case DSA_MC_ADD: | |
115 | ether_addr_copy(mdb.addr, addr); | |
116 | mdb.vid = vid; | |
117 | ||
118 | err = dsa_port_standalone_host_mdb_add(dp, &mdb); | |
119 | if (err) { | |
120 | dev_err(ds->dev, | |
121 | "port %d failed to add %pM vid %d to mdb: %d\n", | |
122 | dp->index, addr, vid, err); | |
123 | break; | |
124 | } | |
125 | break; | |
126 | case DSA_MC_DEL: | |
127 | ether_addr_copy(mdb.addr, addr); | |
128 | mdb.vid = vid; | |
129 | ||
130 | err = dsa_port_standalone_host_mdb_del(dp, &mdb); | |
131 | if (err) { | |
132 | dev_err(ds->dev, | |
133 | "port %d failed to delete %pM vid %d from mdb: %d\n", | |
134 | dp->index, addr, vid, err); | |
135 | } | |
136 | ||
137 | break; | |
138 | } | |
139 | ||
140 | kfree(standalone_work); | |
141 | } | |
142 | ||
6ca80638 FF |
143 | static int dsa_user_schedule_standalone_work(struct net_device *dev, |
144 | enum dsa_standalone_event event, | |
145 | const unsigned char *addr, | |
146 | u16 vid) | |
5e8a1e03 VO |
147 | { |
148 | struct dsa_standalone_event_work *standalone_work; | |
149 | ||
150 | standalone_work = kzalloc(sizeof(*standalone_work), GFP_ATOMIC); | |
151 | if (!standalone_work) | |
152 | return -ENOMEM; | |
153 | ||
6ca80638 | 154 | INIT_WORK(&standalone_work->work, dsa_user_standalone_event_work); |
5e8a1e03 VO |
155 | standalone_work->event = event; |
156 | standalone_work->dev = dev; | |
157 | ||
158 | ether_addr_copy(standalone_work->addr, addr); | |
159 | standalone_work->vid = vid; | |
160 | ||
161 | dsa_schedule_work(&standalone_work->work); | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
6ca80638 | 166 | static int dsa_user_host_vlan_rx_filtering(void *arg, int vid) |
64fdc5f3 VO |
167 | { |
168 | struct dsa_host_vlan_rx_filtering_ctx *ctx = arg; | |
169 | ||
6ca80638 | 170 | return dsa_user_schedule_standalone_work(ctx->dev, ctx->event, |
64fdc5f3 VO |
171 | ctx->addr, vid); |
172 | } | |
173 | ||
6ca80638 FF |
174 | static int dsa_user_vlan_for_each(struct net_device *dev, |
175 | int (*cb)(void *arg, int vid), void *arg) | |
d06f925f | 176 | { |
6ca80638 | 177 | struct dsa_port *dp = dsa_user_to_port(dev); |
d06f925f VO |
178 | struct dsa_vlan *v; |
179 | int err; | |
180 | ||
181 | lockdep_assert_held(&dev->addr_list_lock); | |
182 | ||
183 | err = cb(arg, 0); | |
184 | if (err) | |
185 | return err; | |
186 | ||
187 | list_for_each_entry(v, &dp->user_vlans, list) { | |
188 | err = cb(arg, v->vid); | |
189 | if (err) | |
190 | return err; | |
191 | } | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
6ca80638 FF |
196 | static int dsa_user_sync_uc(struct net_device *dev, |
197 | const unsigned char *addr) | |
5e8a1e03 | 198 | { |
6ca80638 FF |
199 | struct net_device *conduit = dsa_user_to_conduit(dev); |
200 | struct dsa_port *dp = dsa_user_to_port(dev); | |
64fdc5f3 VO |
201 | struct dsa_host_vlan_rx_filtering_ctx ctx = { |
202 | .dev = dev, | |
203 | .addr = addr, | |
204 | .event = DSA_UC_ADD, | |
205 | }; | |
5077e2c8 | 206 | |
6ca80638 | 207 | dev_uc_add(conduit, addr); |
5077e2c8 VO |
208 | |
209 | if (!dsa_switch_supports_uc_filtering(dp->ds)) | |
210 | return 0; | |
211 | ||
6ca80638 | 212 | return dsa_user_vlan_for_each(dev, dsa_user_host_vlan_rx_filtering, |
d06f925f | 213 | &ctx); |
5e8a1e03 VO |
214 | } |
215 | ||
6ca80638 FF |
216 | static int dsa_user_unsync_uc(struct net_device *dev, |
217 | const unsigned char *addr) | |
5e8a1e03 | 218 | { |
6ca80638 FF |
219 | struct net_device *conduit = dsa_user_to_conduit(dev); |
220 | struct dsa_port *dp = dsa_user_to_port(dev); | |
64fdc5f3 VO |
221 | struct dsa_host_vlan_rx_filtering_ctx ctx = { |
222 | .dev = dev, | |
223 | .addr = addr, | |
224 | .event = DSA_UC_DEL, | |
225 | }; | |
5077e2c8 | 226 | |
6ca80638 | 227 | dev_uc_del(conduit, addr); |
5077e2c8 VO |
228 | |
229 | if (!dsa_switch_supports_uc_filtering(dp->ds)) | |
230 | return 0; | |
231 | ||
6ca80638 | 232 | return dsa_user_vlan_for_each(dev, dsa_user_host_vlan_rx_filtering, |
d06f925f | 233 | &ctx); |
5e8a1e03 VO |
234 | } |
235 | ||
6ca80638 FF |
236 | static int dsa_user_sync_mc(struct net_device *dev, |
237 | const unsigned char *addr) | |
5e8a1e03 | 238 | { |
6ca80638 FF |
239 | struct net_device *conduit = dsa_user_to_conduit(dev); |
240 | struct dsa_port *dp = dsa_user_to_port(dev); | |
64fdc5f3 VO |
241 | struct dsa_host_vlan_rx_filtering_ctx ctx = { |
242 | .dev = dev, | |
243 | .addr = addr, | |
244 | .event = DSA_MC_ADD, | |
245 | }; | |
5077e2c8 | 246 | |
6ca80638 | 247 | dev_mc_add(conduit, addr); |
5077e2c8 VO |
248 | |
249 | if (!dsa_switch_supports_mc_filtering(dp->ds)) | |
250 | return 0; | |
251 | ||
6ca80638 | 252 | return dsa_user_vlan_for_each(dev, dsa_user_host_vlan_rx_filtering, |
d06f925f | 253 | &ctx); |
5e8a1e03 VO |
254 | } |
255 | ||
6ca80638 FF |
256 | static int dsa_user_unsync_mc(struct net_device *dev, |
257 | const unsigned char *addr) | |
5e8a1e03 | 258 | { |
6ca80638 FF |
259 | struct net_device *conduit = dsa_user_to_conduit(dev); |
260 | struct dsa_port *dp = dsa_user_to_port(dev); | |
64fdc5f3 VO |
261 | struct dsa_host_vlan_rx_filtering_ctx ctx = { |
262 | .dev = dev, | |
263 | .addr = addr, | |
264 | .event = DSA_MC_DEL, | |
265 | }; | |
5077e2c8 | 266 | |
6ca80638 | 267 | dev_mc_del(conduit, addr); |
5077e2c8 VO |
268 | |
269 | if (!dsa_switch_supports_mc_filtering(dp->ds)) | |
270 | return 0; | |
271 | ||
6ca80638 | 272 | return dsa_user_vlan_for_each(dev, dsa_user_host_vlan_rx_filtering, |
d06f925f | 273 | &ctx); |
5e8a1e03 VO |
274 | } |
275 | ||
6ca80638 | 276 | void dsa_user_sync_ha(struct net_device *dev) |
95f510d0 | 277 | { |
6ca80638 | 278 | struct dsa_port *dp = dsa_user_to_port(dev); |
95f510d0 VO |
279 | struct dsa_switch *ds = dp->ds; |
280 | struct netdev_hw_addr *ha; | |
281 | ||
282 | netif_addr_lock_bh(dev); | |
283 | ||
284 | netdev_for_each_synced_mc_addr(ha, dev) | |
6ca80638 | 285 | dsa_user_sync_mc(dev, ha->addr); |
95f510d0 VO |
286 | |
287 | netdev_for_each_synced_uc_addr(ha, dev) | |
6ca80638 | 288 | dsa_user_sync_uc(dev, ha->addr); |
95f510d0 VO |
289 | |
290 | netif_addr_unlock_bh(dev); | |
291 | ||
292 | if (dsa_switch_supports_uc_filtering(ds) || | |
293 | dsa_switch_supports_mc_filtering(ds)) | |
294 | dsa_flush_workqueue(); | |
295 | } | |
296 | ||
6ca80638 | 297 | void dsa_user_unsync_ha(struct net_device *dev) |
95f510d0 | 298 | { |
6ca80638 | 299 | struct dsa_port *dp = dsa_user_to_port(dev); |
95f510d0 VO |
300 | struct dsa_switch *ds = dp->ds; |
301 | struct netdev_hw_addr *ha; | |
302 | ||
303 | netif_addr_lock_bh(dev); | |
304 | ||
305 | netdev_for_each_synced_uc_addr(ha, dev) | |
6ca80638 | 306 | dsa_user_unsync_uc(dev, ha->addr); |
95f510d0 VO |
307 | |
308 | netdev_for_each_synced_mc_addr(ha, dev) | |
6ca80638 | 309 | dsa_user_unsync_mc(dev, ha->addr); |
95f510d0 VO |
310 | |
311 | netif_addr_unlock_bh(dev); | |
312 | ||
313 | if (dsa_switch_supports_uc_filtering(ds) || | |
314 | dsa_switch_supports_mc_filtering(ds)) | |
315 | dsa_flush_workqueue(); | |
316 | } | |
317 | ||
6ca80638 FF |
318 | /* user mii_bus handling ***************************************************/ |
319 | static int dsa_user_phy_read(struct mii_bus *bus, int addr, int reg) | |
91da11f8 LB |
320 | { |
321 | struct dsa_switch *ds = bus->priv; | |
322 | ||
0d8bcdd3 | 323 | if (ds->phys_mii_mask & (1 << addr)) |
9d490b4e | 324 | return ds->ops->phy_read(ds, addr, reg); |
91da11f8 LB |
325 | |
326 | return 0xffff; | |
327 | } | |
328 | ||
6ca80638 | 329 | static int dsa_user_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) |
91da11f8 LB |
330 | { |
331 | struct dsa_switch *ds = bus->priv; | |
332 | ||
0d8bcdd3 | 333 | if (ds->phys_mii_mask & (1 << addr)) |
9d490b4e | 334 | return ds->ops->phy_write(ds, addr, reg, val); |
91da11f8 LB |
335 | |
336 | return 0; | |
337 | } | |
338 | ||
6ca80638 | 339 | void dsa_user_mii_bus_init(struct dsa_switch *ds) |
91da11f8 | 340 | { |
6ca80638 FF |
341 | ds->user_mii_bus->priv = (void *)ds; |
342 | ds->user_mii_bus->name = "dsa user smi"; | |
343 | ds->user_mii_bus->read = dsa_user_phy_read; | |
344 | ds->user_mii_bus->write = dsa_user_phy_write; | |
345 | snprintf(ds->user_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d", | |
49463b7f | 346 | ds->dst->index, ds->index); |
6ca80638 FF |
347 | ds->user_mii_bus->parent = ds->dev; |
348 | ds->user_mii_bus->phy_mask = ~ds->phys_mii_mask; | |
91da11f8 LB |
349 | } |
350 | ||
351 | ||
6ca80638 FF |
352 | /* user device handling ****************************************************/ |
353 | static int dsa_user_get_iflink(const struct net_device *dev) | |
c0840801 | 354 | { |
6ca80638 | 355 | return dsa_user_to_conduit(dev)->ifindex; |
c0840801 LB |
356 | } |
357 | ||
6ca80638 | 358 | static int dsa_user_open(struct net_device *dev) |
91da11f8 | 359 | { |
6ca80638 FF |
360 | struct net_device *conduit = dsa_user_to_conduit(dev); |
361 | struct dsa_port *dp = dsa_user_to_port(dev); | |
499aa9e1 | 362 | struct dsa_switch *ds = dp->ds; |
df02c6ff LB |
363 | int err; |
364 | ||
6ca80638 | 365 | err = dev_open(conduit, NULL); |
9d5ef190 | 366 | if (err < 0) { |
6ca80638 | 367 | netdev_err(dev, "failed to open conduit %s\n", conduit->name); |
9d5ef190 VO |
368 | goto out; |
369 | } | |
df02c6ff | 370 | |
499aa9e1 VO |
371 | if (dsa_switch_supports_uc_filtering(ds)) { |
372 | err = dsa_port_standalone_host_fdb_add(dp, dev->dev_addr, 0); | |
373 | if (err) | |
374 | goto out; | |
375 | } | |
376 | ||
6ca80638 FF |
377 | if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) { |
378 | err = dev_uc_add(conduit, dev->dev_addr); | |
df02c6ff | 379 | if (err < 0) |
499aa9e1 | 380 | goto del_host_addr; |
df02c6ff LB |
381 | } |
382 | ||
8640f8dc | 383 | err = dsa_port_enable_rt(dp, dev->phydev); |
fb8a6a2b | 384 | if (err) |
35aae5ab | 385 | goto del_unicast; |
b73adef6 | 386 | |
91da11f8 | 387 | return 0; |
df02c6ff | 388 | |
df02c6ff | 389 | del_unicast: |
6ca80638 FF |
390 | if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) |
391 | dev_uc_del(conduit, dev->dev_addr); | |
499aa9e1 VO |
392 | del_host_addr: |
393 | if (dsa_switch_supports_uc_filtering(ds)) | |
394 | dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); | |
df02c6ff LB |
395 | out: |
396 | return err; | |
91da11f8 LB |
397 | } |
398 | ||
6ca80638 | 399 | static int dsa_user_close(struct net_device *dev) |
91da11f8 | 400 | { |
6ca80638 FF |
401 | struct net_device *conduit = dsa_user_to_conduit(dev); |
402 | struct dsa_port *dp = dsa_user_to_port(dev); | |
499aa9e1 | 403 | struct dsa_switch *ds = dp->ds; |
df02c6ff | 404 | |
8640f8dc | 405 | dsa_port_disable_rt(dp); |
6457edfe | 406 | |
6ca80638 FF |
407 | if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) |
408 | dev_uc_del(conduit, dev->dev_addr); | |
df02c6ff | 409 | |
499aa9e1 VO |
410 | if (dsa_switch_supports_uc_filtering(ds)) |
411 | dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); | |
412 | ||
91da11f8 LB |
413 | return 0; |
414 | } | |
415 | ||
6ca80638 | 416 | static void dsa_user_manage_host_flood(struct net_device *dev) |
7569459a | 417 | { |
72c3b0c7 | 418 | bool mc = dev->flags & (IFF_PROMISC | IFF_ALLMULTI); |
6ca80638 | 419 | struct dsa_port *dp = dsa_user_to_port(dev); |
72c3b0c7 | 420 | bool uc = dev->flags & IFF_PROMISC; |
7569459a | 421 | |
72c3b0c7 | 422 | dsa_port_set_host_flood(dp, uc, mc); |
7569459a VO |
423 | } |
424 | ||
6ca80638 | 425 | static void dsa_user_change_rx_flags(struct net_device *dev, int change) |
91da11f8 | 426 | { |
6ca80638 FF |
427 | struct net_device *conduit = dsa_user_to_conduit(dev); |
428 | struct dsa_port *dp = dsa_user_to_port(dev); | |
7569459a | 429 | struct dsa_switch *ds = dp->ds; |
35aae5ab VO |
430 | |
431 | if (change & IFF_ALLMULTI) | |
6ca80638 | 432 | dev_set_allmulti(conduit, |
35aae5ab VO |
433 | dev->flags & IFF_ALLMULTI ? 1 : -1); |
434 | if (change & IFF_PROMISC) | |
6ca80638 | 435 | dev_set_promiscuity(conduit, |
35aae5ab | 436 | dev->flags & IFF_PROMISC ? 1 : -1); |
7569459a VO |
437 | |
438 | if (dsa_switch_supports_uc_filtering(ds) && | |
439 | dsa_switch_supports_mc_filtering(ds)) | |
6ca80638 | 440 | dsa_user_manage_host_flood(dev); |
91da11f8 LB |
441 | } |
442 | ||
6ca80638 | 443 | static void dsa_user_set_rx_mode(struct net_device *dev) |
91da11f8 | 444 | { |
6ca80638 FF |
445 | __dev_mc_sync(dev, dsa_user_sync_mc, dsa_user_unsync_mc); |
446 | __dev_uc_sync(dev, dsa_user_sync_uc, dsa_user_unsync_uc); | |
91da11f8 LB |
447 | } |
448 | ||
6ca80638 | 449 | static int dsa_user_set_mac_address(struct net_device *dev, void *a) |
91da11f8 | 450 | { |
6ca80638 FF |
451 | struct net_device *conduit = dsa_user_to_conduit(dev); |
452 | struct dsa_port *dp = dsa_user_to_port(dev); | |
499aa9e1 | 453 | struct dsa_switch *ds = dp->ds; |
df02c6ff LB |
454 | struct sockaddr *addr = a; |
455 | int err; | |
456 | ||
457 | if (!is_valid_ether_addr(addr->sa_data)) | |
458 | return -EADDRNOTAVAIL; | |
459 | ||
6715042c VO |
460 | if (ds->ops->port_set_mac_address) { |
461 | err = ds->ops->port_set_mac_address(ds, dp->index, | |
462 | addr->sa_data); | |
463 | if (err) | |
464 | return err; | |
465 | } | |
466 | ||
e2d0576f | 467 | /* If the port is down, the address isn't synced yet to hardware or |
6ca80638 | 468 | * to the DSA conduit, so there is nothing to change. |
e2d0576f VO |
469 | */ |
470 | if (!(dev->flags & IFF_UP)) | |
471 | goto out_change_dev_addr; | |
472 | ||
499aa9e1 VO |
473 | if (dsa_switch_supports_uc_filtering(ds)) { |
474 | err = dsa_port_standalone_host_fdb_add(dp, addr->sa_data, 0); | |
475 | if (err) | |
476 | return err; | |
477 | } | |
478 | ||
6ca80638 FF |
479 | if (!ether_addr_equal(addr->sa_data, conduit->dev_addr)) { |
480 | err = dev_uc_add(conduit, addr->sa_data); | |
df02c6ff | 481 | if (err < 0) |
499aa9e1 | 482 | goto del_unicast; |
df02c6ff LB |
483 | } |
484 | ||
6ca80638 FF |
485 | if (!ether_addr_equal(dev->dev_addr, conduit->dev_addr)) |
486 | dev_uc_del(conduit, dev->dev_addr); | |
df02c6ff | 487 | |
499aa9e1 VO |
488 | if (dsa_switch_supports_uc_filtering(ds)) |
489 | dsa_port_standalone_host_fdb_del(dp, dev->dev_addr, 0); | |
490 | ||
e2d0576f | 491 | out_change_dev_addr: |
e35b8d7d | 492 | eth_hw_addr_set(dev, addr->sa_data); |
91da11f8 LB |
493 | |
494 | return 0; | |
499aa9e1 VO |
495 | |
496 | del_unicast: | |
497 | if (dsa_switch_supports_uc_filtering(ds)) | |
498 | dsa_port_standalone_host_fdb_del(dp, addr->sa_data, 0); | |
499 | ||
500 | return err; | |
91da11f8 LB |
501 | } |
502 | ||
6ca80638 | 503 | struct dsa_user_dump_ctx { |
2bedde1a AS |
504 | struct net_device *dev; |
505 | struct sk_buff *skb; | |
506 | struct netlink_callback *cb; | |
507 | int idx; | |
508 | }; | |
509 | ||
510 | static int | |
6ca80638 FF |
511 | dsa_user_port_fdb_do_dump(const unsigned char *addr, u16 vid, |
512 | bool is_static, void *data) | |
2bedde1a | 513 | { |
6ca80638 | 514 | struct dsa_user_dump_ctx *dump = data; |
2bedde1a AS |
515 | u32 portid = NETLINK_CB(dump->cb->skb).portid; |
516 | u32 seq = dump->cb->nlh->nlmsg_seq; | |
517 | struct nlmsghdr *nlh; | |
518 | struct ndmsg *ndm; | |
519 | ||
520 | if (dump->idx < dump->cb->args[2]) | |
521 | goto skip; | |
522 | ||
523 | nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, | |
524 | sizeof(*ndm), NLM_F_MULTI); | |
525 | if (!nlh) | |
526 | return -EMSGSIZE; | |
527 | ||
528 | ndm = nlmsg_data(nlh); | |
529 | ndm->ndm_family = AF_BRIDGE; | |
530 | ndm->ndm_pad1 = 0; | |
531 | ndm->ndm_pad2 = 0; | |
532 | ndm->ndm_flags = NTF_SELF; | |
533 | ndm->ndm_type = 0; | |
534 | ndm->ndm_ifindex = dump->dev->ifindex; | |
535 | ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; | |
536 | ||
537 | if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) | |
538 | goto nla_put_failure; | |
539 | ||
540 | if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) | |
541 | goto nla_put_failure; | |
542 | ||
543 | nlmsg_end(dump->skb, nlh); | |
544 | ||
545 | skip: | |
546 | dump->idx++; | |
547 | return 0; | |
548 | ||
549 | nla_put_failure: | |
550 | nlmsg_cancel(dump->skb, nlh); | |
551 | return -EMSGSIZE; | |
552 | } | |
553 | ||
554 | static int | |
6ca80638 FF |
555 | dsa_user_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, |
556 | struct net_device *dev, struct net_device *filter_dev, | |
557 | int *idx) | |
2bedde1a | 558 | { |
6ca80638 FF |
559 | struct dsa_port *dp = dsa_user_to_port(dev); |
560 | struct dsa_user_dump_ctx dump = { | |
2bedde1a AS |
561 | .dev = dev, |
562 | .skb = skb, | |
563 | .cb = cb, | |
564 | .idx = *idx, | |
565 | }; | |
2bedde1a AS |
566 | int err; |
567 | ||
6ca80638 | 568 | err = dsa_port_fdb_dump(dp, dsa_user_port_fdb_do_dump, &dump); |
2bedde1a | 569 | *idx = dump.idx; |
de40fc5d | 570 | |
2bedde1a AS |
571 | return err; |
572 | } | |
573 | ||
6ca80638 | 574 | static int dsa_user_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
91da11f8 | 575 | { |
6ca80638 | 576 | struct dsa_user_priv *p = netdev_priv(dev); |
0336369d BS |
577 | struct dsa_switch *ds = p->dp->ds; |
578 | int port = p->dp->index; | |
579 | ||
580 | /* Pass through to switch driver if it supports timestamping */ | |
581 | switch (cmd) { | |
582 | case SIOCGHWTSTAMP: | |
583 | if (ds->ops->port_hwtstamp_get) | |
584 | return ds->ops->port_hwtstamp_get(ds, port, ifr); | |
585 | break; | |
586 | case SIOCSHWTSTAMP: | |
587 | if (ds->ops->port_hwtstamp_set) | |
588 | return ds->ops->port_hwtstamp_set(ds, port, ifr); | |
589 | break; | |
590 | } | |
591 | ||
aab9c406 | 592 | return phylink_mii_ioctl(p->dp->pl, ifr, cmd); |
91da11f8 LB |
593 | } |
594 | ||
6ca80638 FF |
595 | static int dsa_user_port_attr_set(struct net_device *dev, const void *ctx, |
596 | const struct switchdev_attr *attr, | |
597 | struct netlink_ext_ack *extack) | |
35636062 | 598 | { |
6ca80638 | 599 | struct dsa_port *dp = dsa_user_to_port(dev); |
b8d866ac | 600 | int ret; |
35636062 | 601 | |
0d2cfbd4 VO |
602 | if (ctx && ctx != dp) |
603 | return 0; | |
604 | ||
35636062 | 605 | switch (attr->id) { |
1f868398 | 606 | case SWITCHDEV_ATTR_ID_PORT_STP_STATE: |
03cbb870 VO |
607 | if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) |
608 | return -EOPNOTSUPP; | |
609 | ||
39f32101 | 610 | ret = dsa_port_set_state(dp, attr->u.stp_state, true); |
35636062 | 611 | break; |
7414af30 TW |
612 | case SWITCHDEV_ATTR_ID_PORT_MST_STATE: |
613 | if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) | |
614 | return -EOPNOTSUPP; | |
615 | ||
616 | ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack); | |
617 | break; | |
fb2dabad | 618 | case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: |
936db8a2 | 619 | if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) |
03cbb870 VO |
620 | return -EOPNOTSUPP; |
621 | ||
89153ed6 VO |
622 | ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering, |
623 | extack); | |
fb2dabad | 624 | break; |
34a79f63 | 625 | case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: |
936db8a2 | 626 | if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) |
03cbb870 VO |
627 | return -EOPNOTSUPP; |
628 | ||
bae33f2b | 629 | ret = dsa_port_ageing_time(dp, attr->u.ageing_time); |
34a79f63 | 630 | break; |
332afc4c TW |
631 | case SWITCHDEV_ATTR_ID_BRIDGE_MST: |
632 | if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) | |
633 | return -EOPNOTSUPP; | |
634 | ||
635 | ret = dsa_port_mst_enable(dp, attr->u.mst, extack); | |
636 | break; | |
ea87005a | 637 | case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: |
03cbb870 VO |
638 | if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) |
639 | return -EOPNOTSUPP; | |
640 | ||
a8b659e7 VO |
641 | ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags, |
642 | extack); | |
ea87005a | 643 | break; |
57652796 | 644 | case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: |
03cbb870 VO |
645 | if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev)) |
646 | return -EOPNOTSUPP; | |
647 | ||
a8b659e7 | 648 | ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack); |
57652796 | 649 | break; |
8e6598a7 TW |
650 | case SWITCHDEV_ATTR_ID_VLAN_MSTI: |
651 | if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) | |
652 | return -EOPNOTSUPP; | |
653 | ||
654 | ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti); | |
655 | break; | |
35636062 SF |
656 | default: |
657 | ret = -EOPNOTSUPP; | |
658 | break; | |
659 | } | |
660 | ||
661 | return ret; | |
662 | } | |
663 | ||
1ce39f0e VO |
664 | /* Must be called under rcu_read_lock() */ |
665 | static int | |
6ca80638 FF |
666 | dsa_user_vlan_check_for_8021q_uppers(struct net_device *user, |
667 | const struct switchdev_obj_port_vlan *vlan) | |
1ce39f0e VO |
668 | { |
669 | struct net_device *upper_dev; | |
670 | struct list_head *iter; | |
671 | ||
6ca80638 | 672 | netdev_for_each_upper_dev_rcu(user, upper_dev, iter) { |
1ce39f0e VO |
673 | u16 vid; |
674 | ||
675 | if (!is_vlan_dev(upper_dev)) | |
676 | continue; | |
677 | ||
678 | vid = vlan_dev_vlan_id(upper_dev); | |
b7a9e0da | 679 | if (vid == vlan->vid) |
1ce39f0e VO |
680 | return -EBUSY; |
681 | } | |
682 | ||
683 | return 0; | |
684 | } | |
685 | ||
6ca80638 FF |
686 | static int dsa_user_vlan_add(struct net_device *dev, |
687 | const struct switchdev_obj *obj, | |
688 | struct netlink_ext_ack *extack) | |
bdcff080 | 689 | { |
6ca80638 | 690 | struct dsa_port *dp = dsa_user_to_port(dev); |
134ef238 | 691 | struct switchdev_obj_port_vlan *vlan; |
b7a9e0da | 692 | int err; |
bdcff080 | 693 | |
0ee2af4e VO |
694 | if (dsa_port_skip_vlan_configuration(dp)) { |
695 | NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN"); | |
c5335d73 | 696 | return 0; |
0ee2af4e | 697 | } |
c5335d73 | 698 | |
134ef238 | 699 | vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); |
bdcff080 | 700 | |
1ce39f0e VO |
701 | /* Deny adding a bridge VLAN when there is already an 802.1Q upper with |
702 | * the same VID. | |
703 | */ | |
36cbf39b | 704 | if (br_vlan_enabled(dsa_port_bridge_dev_get(dp))) { |
1ce39f0e | 705 | rcu_read_lock(); |
6ca80638 | 706 | err = dsa_user_vlan_check_for_8021q_uppers(dev, vlan); |
1ce39f0e | 707 | rcu_read_unlock(); |
31046a5f VO |
708 | if (err) { |
709 | NL_SET_ERR_MSG_MOD(extack, | |
710 | "Port already has a VLAN upper with this VID"); | |
1ce39f0e | 711 | return err; |
31046a5f | 712 | } |
1ce39f0e VO |
713 | } |
714 | ||
134ef238 VO |
715 | return dsa_port_vlan_add(dp, vlan, extack); |
716 | } | |
717 | ||
164f861b VO |
718 | /* Offload a VLAN installed on the bridge or on a foreign interface by |
719 | * installing it as a VLAN towards the CPU port. | |
720 | */ | |
6ca80638 FF |
721 | static int dsa_user_host_vlan_add(struct net_device *dev, |
722 | const struct switchdev_obj *obj, | |
723 | struct netlink_ext_ack *extack) | |
134ef238 | 724 | { |
6ca80638 | 725 | struct dsa_port *dp = dsa_user_to_port(dev); |
134ef238 | 726 | struct switchdev_obj_port_vlan vlan; |
bdcff080 | 727 | |
164f861b VO |
728 | /* Do nothing if this is a software bridge */ |
729 | if (!dp->bridge) | |
730 | return -EOPNOTSUPP; | |
731 | ||
134ef238 VO |
732 | if (dsa_port_skip_vlan_configuration(dp)) { |
733 | NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN"); | |
734 | return 0; | |
735 | } | |
736 | ||
737 | vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj); | |
738 | ||
739 | /* Even though drivers often handle CPU membership in special ways, | |
b9499904 VD |
740 | * it doesn't make sense to program a PVID, so clear this flag. |
741 | */ | |
742 | vlan.flags &= ~BRIDGE_VLAN_INFO_PVID; | |
743 | ||
134ef238 | 744 | return dsa_port_host_vlan_add(dp, &vlan, extack); |
bdcff080 VD |
745 | } |
746 | ||
6ca80638 FF |
747 | static int dsa_user_port_obj_add(struct net_device *dev, const void *ctx, |
748 | const struct switchdev_obj *obj, | |
749 | struct netlink_ext_ack *extack) | |
ba14d9eb | 750 | { |
6ca80638 | 751 | struct dsa_port *dp = dsa_user_to_port(dev); |
ba14d9eb VD |
752 | int err; |
753 | ||
0d2cfbd4 VO |
754 | if (ctx && ctx != dp) |
755 | return 0; | |
756 | ||
9e8f4a54 | 757 | switch (obj->id) { |
8df30255 | 758 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
03cbb870 | 759 | if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) |
79b139f4 | 760 | return -EOPNOTSUPP; |
03cbb870 | 761 | |
ffb68fc5 | 762 | err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); |
8df30255 | 763 | break; |
5f4dbc50 | 764 | case SWITCHDEV_OBJ_ID_HOST_MDB: |
936db8a2 | 765 | if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) |
03cbb870 VO |
766 | return -EOPNOTSUPP; |
767 | ||
68d6d71e | 768 | err = dsa_port_bridge_host_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); |
5f4dbc50 | 769 | break; |
57d80838 | 770 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
164f861b | 771 | if (dsa_port_offloads_bridge_port(dp, obj->orig_dev)) |
6ca80638 | 772 | err = dsa_user_vlan_add(dev, obj, extack); |
164f861b | 773 | else |
6ca80638 | 774 | err = dsa_user_host_vlan_add(dev, obj, extack); |
11149536 | 775 | break; |
c595c433 | 776 | case SWITCHDEV_OBJ_ID_MRP: |
936db8a2 | 777 | if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) |
c595c433 | 778 | return -EOPNOTSUPP; |
03cbb870 | 779 | |
c595c433 HV |
780 | err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj)); |
781 | break; | |
782 | case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: | |
936db8a2 | 783 | if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) |
c595c433 | 784 | return -EOPNOTSUPP; |
03cbb870 | 785 | |
c595c433 HV |
786 | err = dsa_port_mrp_add_ring_role(dp, |
787 | SWITCHDEV_OBJ_RING_ROLE_MRP(obj)); | |
788 | break; | |
ba14d9eb VD |
789 | default: |
790 | err = -EOPNOTSUPP; | |
791 | break; | |
792 | } | |
793 | ||
794 | return err; | |
795 | } | |
796 | ||
6ca80638 FF |
797 | static int dsa_user_vlan_del(struct net_device *dev, |
798 | const struct switchdev_obj *obj) | |
bdcff080 | 799 | { |
6ca80638 | 800 | struct dsa_port *dp = dsa_user_to_port(dev); |
2209158c | 801 | struct switchdev_obj_port_vlan *vlan; |
bdcff080 | 802 | |
54a0ed0d | 803 | if (dsa_port_skip_vlan_configuration(dp)) |
c5335d73 VD |
804 | return 0; |
805 | ||
2209158c VO |
806 | vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); |
807 | ||
134ef238 VO |
808 | return dsa_port_vlan_del(dp, vlan); |
809 | } | |
2209158c | 810 | |
6ca80638 FF |
811 | static int dsa_user_host_vlan_del(struct net_device *dev, |
812 | const struct switchdev_obj *obj) | |
134ef238 | 813 | { |
6ca80638 | 814 | struct dsa_port *dp = dsa_user_to_port(dev); |
134ef238 | 815 | struct switchdev_obj_port_vlan *vlan; |
2209158c | 816 | |
164f861b VO |
817 | /* Do nothing if this is a software bridge */ |
818 | if (!dp->bridge) | |
819 | return -EOPNOTSUPP; | |
820 | ||
134ef238 VO |
821 | if (dsa_port_skip_vlan_configuration(dp)) |
822 | return 0; | |
823 | ||
824 | vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); | |
825 | ||
826 | return dsa_port_host_vlan_del(dp, vlan); | |
bdcff080 VD |
827 | } |
828 | ||
6ca80638 FF |
829 | static int dsa_user_port_obj_del(struct net_device *dev, const void *ctx, |
830 | const struct switchdev_obj *obj) | |
ba14d9eb | 831 | { |
6ca80638 | 832 | struct dsa_port *dp = dsa_user_to_port(dev); |
ba14d9eb VD |
833 | int err; |
834 | ||
0d2cfbd4 VO |
835 | if (ctx && ctx != dp) |
836 | return 0; | |
837 | ||
9e8f4a54 | 838 | switch (obj->id) { |
8df30255 | 839 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
03cbb870 | 840 | if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) |
79b139f4 | 841 | return -EOPNOTSUPP; |
03cbb870 | 842 | |
bcebb976 | 843 | err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); |
8df30255 | 844 | break; |
5f4dbc50 | 845 | case SWITCHDEV_OBJ_ID_HOST_MDB: |
936db8a2 | 846 | if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) |
03cbb870 VO |
847 | return -EOPNOTSUPP; |
848 | ||
68d6d71e | 849 | err = dsa_port_bridge_host_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); |
5f4dbc50 | 850 | break; |
57d80838 | 851 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
164f861b | 852 | if (dsa_port_offloads_bridge_port(dp, obj->orig_dev)) |
6ca80638 | 853 | err = dsa_user_vlan_del(dev, obj); |
164f861b | 854 | else |
6ca80638 | 855 | err = dsa_user_host_vlan_del(dev, obj); |
11149536 | 856 | break; |
c595c433 | 857 | case SWITCHDEV_OBJ_ID_MRP: |
936db8a2 | 858 | if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) |
c595c433 | 859 | return -EOPNOTSUPP; |
03cbb870 | 860 | |
c595c433 HV |
861 | err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj)); |
862 | break; | |
863 | case SWITCHDEV_OBJ_ID_RING_ROLE_MRP: | |
936db8a2 | 864 | if (!dsa_port_offloads_bridge_dev(dp, obj->orig_dev)) |
c595c433 | 865 | return -EOPNOTSUPP; |
03cbb870 | 866 | |
c595c433 HV |
867 | err = dsa_port_mrp_del_ring_role(dp, |
868 | SWITCHDEV_OBJ_RING_ROLE_MRP(obj)); | |
869 | break; | |
ba14d9eb VD |
870 | default: |
871 | err = -EOPNOTSUPP; | |
872 | break; | |
873 | } | |
874 | ||
875 | return err; | |
876 | } | |
877 | ||
6ca80638 FF |
878 | static inline netdev_tx_t dsa_user_netpoll_send_skb(struct net_device *dev, |
879 | struct sk_buff *skb) | |
04ff53f9 FF |
880 | { |
881 | #ifdef CONFIG_NET_POLL_CONTROLLER | |
6ca80638 | 882 | struct dsa_user_priv *p = netdev_priv(dev); |
4fa7b718 | 883 | |
f78ed220 | 884 | return netpoll_send_skb(p->netpoll, skb); |
04ff53f9 FF |
885 | #else |
886 | BUG(); | |
04ff53f9 | 887 | return NETDEV_TX_OK; |
f78ed220 | 888 | #endif |
04ff53f9 FF |
889 | } |
890 | ||
6ca80638 | 891 | static void dsa_skb_tx_timestamp(struct dsa_user_priv *p, |
90af1059 BS |
892 | struct sk_buff *skb) |
893 | { | |
894 | struct dsa_switch *ds = p->dp->ds; | |
90af1059 | 895 | |
cfd12c06 YL |
896 | if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) |
897 | return; | |
898 | ||
90af1059 BS |
899 | if (!ds->ops->port_txtstamp) |
900 | return; | |
901 | ||
5c5416f5 | 902 | ds->ops->port_txtstamp(ds, p->dp->index, skb); |
90af1059 BS |
903 | } |
904 | ||
97a69a0d VO |
905 | netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev) |
906 | { | |
907 | /* SKB for netpoll still need to be mangled with the protocol-specific | |
908 | * tag to be successfully transmitted | |
909 | */ | |
910 | if (unlikely(netpoll_tx_running(dev))) | |
6ca80638 | 911 | return dsa_user_netpoll_send_skb(dev, skb); |
97a69a0d VO |
912 | |
913 | /* Queue the SKB for transmission on the parent interface, but | |
914 | * do not modify its EtherType | |
915 | */ | |
6ca80638 | 916 | skb->dev = dsa_user_to_conduit(dev); |
97a69a0d VO |
917 | dev_queue_xmit(skb); |
918 | ||
919 | return NETDEV_TX_OK; | |
920 | } | |
921 | EXPORT_SYMBOL_GPL(dsa_enqueue_skb); | |
922 | ||
a3b0b647 VO |
923 | static int dsa_realloc_skb(struct sk_buff *skb, struct net_device *dev) |
924 | { | |
925 | int needed_headroom = dev->needed_headroom; | |
926 | int needed_tailroom = dev->needed_tailroom; | |
927 | ||
928 | /* For tail taggers, we need to pad short frames ourselves, to ensure | |
929 | * that the tail tag does not fail at its role of being at the end of | |
6ca80638 | 930 | * the packet, once the conduit interface pads the frame. Account for |
a3b0b647 VO |
931 | * that pad length here, and pad later. |
932 | */ | |
933 | if (unlikely(needed_tailroom && skb->len < ETH_ZLEN)) | |
934 | needed_tailroom += ETH_ZLEN - skb->len; | |
935 | /* skb_headroom() returns unsigned int... */ | |
936 | needed_headroom = max_t(int, needed_headroom - skb_headroom(skb), 0); | |
937 | needed_tailroom = max_t(int, needed_tailroom - skb_tailroom(skb), 0); | |
938 | ||
939 | if (likely(!needed_headroom && !needed_tailroom && !skb_cloned(skb))) | |
940 | /* No reallocation needed, yay! */ | |
941 | return 0; | |
942 | ||
943 | return pskb_expand_head(skb, needed_headroom, needed_tailroom, | |
944 | GFP_ATOMIC); | |
945 | } | |
946 | ||
6ca80638 | 947 | static netdev_tx_t dsa_user_xmit(struct sk_buff *skb, struct net_device *dev) |
3e8a72d1 | 948 | { |
6ca80638 | 949 | struct dsa_user_priv *p = netdev_priv(dev); |
4ed70ce9 | 950 | struct sk_buff *nskb; |
3e8a72d1 | 951 | |
6a900628 | 952 | dev_sw_netstats_tx_add(dev, 1, skb->len); |
3e8a72d1 | 953 | |
c4b364ce | 954 | memset(skb->cb, 0, sizeof(skb->cb)); |
87671375 | 955 | |
cf536ea3 | 956 | /* Handle tx timestamp if any */ |
90af1059 BS |
957 | dsa_skb_tx_timestamp(p, skb); |
958 | ||
a3b0b647 VO |
959 | if (dsa_realloc_skb(skb, dev)) { |
960 | dev_kfree_skb_any(skb); | |
961 | return NETDEV_TX_OK; | |
962 | } | |
963 | ||
964 | /* needed_tailroom should still be 'warm' in the cache line from | |
965 | * dsa_realloc_skb(), which has also ensured that padding is safe. | |
966 | */ | |
967 | if (dev->needed_tailroom) | |
968 | eth_skb_pad(skb); | |
969 | ||
fe47d563 VD |
970 | /* Transmit function may have to reallocate the original SKB, |
971 | * in which case it must have freed it. Only free it here on error. | |
972 | */ | |
4ed70ce9 | 973 | nskb = p->xmit(skb, dev); |
fe47d563 | 974 | if (!nskb) { |
a68578c2 | 975 | kfree_skb(skb); |
4ed70ce9 | 976 | return NETDEV_TX_OK; |
fe47d563 | 977 | } |
5aed85ce | 978 | |
97a69a0d VO |
979 | return dsa_enqueue_skb(nskb, dev); |
980 | } | |
04ff53f9 | 981 | |
91da11f8 | 982 | /* ethtool operations *******************************************************/ |
91da11f8 | 983 | |
6ca80638 FF |
984 | static void dsa_user_get_drvinfo(struct net_device *dev, |
985 | struct ethtool_drvinfo *drvinfo) | |
91da11f8 | 986 | { |
e4d44b3d WS |
987 | strscpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver)); |
988 | strscpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); | |
989 | strscpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); | |
91da11f8 LB |
990 | } |
991 | ||
6ca80638 | 992 | static int dsa_user_get_regs_len(struct net_device *dev) |
3d762a0f | 993 | { |
6ca80638 | 994 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 995 | struct dsa_switch *ds = dp->ds; |
3d762a0f | 996 | |
9d490b4e | 997 | if (ds->ops->get_regs_len) |
d945097b | 998 | return ds->ops->get_regs_len(ds, dp->index); |
3d762a0f GR |
999 | |
1000 | return -EOPNOTSUPP; | |
1001 | } | |
1002 | ||
1003 | static void | |
6ca80638 | 1004 | dsa_user_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p) |
3d762a0f | 1005 | { |
6ca80638 | 1006 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1007 | struct dsa_switch *ds = dp->ds; |
3d762a0f | 1008 | |
9d490b4e | 1009 | if (ds->ops->get_regs) |
d945097b | 1010 | ds->ops->get_regs(ds, dp->index, regs, _p); |
3d762a0f GR |
1011 | } |
1012 | ||
6ca80638 | 1013 | static int dsa_user_nway_reset(struct net_device *dev) |
aab9c406 | 1014 | { |
6ca80638 | 1015 | struct dsa_port *dp = dsa_user_to_port(dev); |
aab9c406 FF |
1016 | |
1017 | return phylink_ethtool_nway_reset(dp->pl); | |
1018 | } | |
1019 | ||
6ca80638 | 1020 | static int dsa_user_get_eeprom_len(struct net_device *dev) |
6793abb4 | 1021 | { |
6ca80638 | 1022 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1023 | struct dsa_switch *ds = dp->ds; |
6793abb4 | 1024 | |
0e576044 | 1025 | if (ds->cd && ds->cd->eeprom_len) |
ff04955c | 1026 | return ds->cd->eeprom_len; |
6793abb4 | 1027 | |
9d490b4e VD |
1028 | if (ds->ops->get_eeprom_len) |
1029 | return ds->ops->get_eeprom_len(ds); | |
6793abb4 GR |
1030 | |
1031 | return 0; | |
1032 | } | |
1033 | ||
6ca80638 FF |
1034 | static int dsa_user_get_eeprom(struct net_device *dev, |
1035 | struct ethtool_eeprom *eeprom, u8 *data) | |
6793abb4 | 1036 | { |
6ca80638 | 1037 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1038 | struct dsa_switch *ds = dp->ds; |
6793abb4 | 1039 | |
9d490b4e VD |
1040 | if (ds->ops->get_eeprom) |
1041 | return ds->ops->get_eeprom(ds, eeprom, data); | |
6793abb4 GR |
1042 | |
1043 | return -EOPNOTSUPP; | |
1044 | } | |
1045 | ||
6ca80638 FF |
1046 | static int dsa_user_set_eeprom(struct net_device *dev, |
1047 | struct ethtool_eeprom *eeprom, u8 *data) | |
6793abb4 | 1048 | { |
6ca80638 | 1049 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1050 | struct dsa_switch *ds = dp->ds; |
6793abb4 | 1051 | |
9d490b4e VD |
1052 | if (ds->ops->set_eeprom) |
1053 | return ds->ops->set_eeprom(ds, eeprom, data); | |
6793abb4 GR |
1054 | |
1055 | return -EOPNOTSUPP; | |
1056 | } | |
1057 | ||
6ca80638 FF |
1058 | static void dsa_user_get_strings(struct net_device *dev, |
1059 | uint32_t stringset, uint8_t *data) | |
91da11f8 | 1060 | { |
6ca80638 | 1061 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1062 | struct dsa_switch *ds = dp->ds; |
91da11f8 LB |
1063 | |
1064 | if (stringset == ETH_SS_STATS) { | |
1065 | int len = ETH_GSTRING_LEN; | |
1066 | ||
5c9f7b04 | 1067 | strscpy_pad(data, "tx_packets", len); |
1068 | strscpy_pad(data + len, "tx_bytes", len); | |
1069 | strscpy_pad(data + 2 * len, "rx_packets", len); | |
1070 | strscpy_pad(data + 3 * len, "rx_bytes", len); | |
9d490b4e | 1071 | if (ds->ops->get_strings) |
89f09048 FF |
1072 | ds->ops->get_strings(ds, dp->index, stringset, |
1073 | data + 4 * len); | |
a71acad9 OR |
1074 | } else if (stringset == ETH_SS_TEST) { |
1075 | net_selftest_get_strings(data); | |
91da11f8 | 1076 | } |
a71acad9 | 1077 | |
91da11f8 LB |
1078 | } |
1079 | ||
6ca80638 FF |
1080 | static void dsa_user_get_ethtool_stats(struct net_device *dev, |
1081 | struct ethtool_stats *stats, | |
1082 | uint64_t *data) | |
91da11f8 | 1083 | { |
6ca80638 | 1084 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1085 | struct dsa_switch *ds = dp->ds; |
5f6b4e14 | 1086 | struct pcpu_sw_netstats *s; |
f613ed66 | 1087 | unsigned int start; |
5f6b4e14 FF |
1088 | int i; |
1089 | ||
1090 | for_each_possible_cpu(i) { | |
1091 | u64 tx_packets, tx_bytes, rx_packets, rx_bytes; | |
1092 | ||
6a900628 | 1093 | s = per_cpu_ptr(dev->tstats, i); |
5f6b4e14 | 1094 | do { |
d120d1a6 | 1095 | start = u64_stats_fetch_begin(&s->syncp); |
9962acef ED |
1096 | tx_packets = u64_stats_read(&s->tx_packets); |
1097 | tx_bytes = u64_stats_read(&s->tx_bytes); | |
1098 | rx_packets = u64_stats_read(&s->rx_packets); | |
1099 | rx_bytes = u64_stats_read(&s->rx_bytes); | |
d120d1a6 | 1100 | } while (u64_stats_fetch_retry(&s->syncp, start)); |
5f6b4e14 FF |
1101 | data[0] += tx_packets; |
1102 | data[1] += tx_bytes; | |
1103 | data[2] += rx_packets; | |
1104 | data[3] += rx_bytes; | |
1105 | } | |
9d490b4e | 1106 | if (ds->ops->get_ethtool_stats) |
d945097b | 1107 | ds->ops->get_ethtool_stats(ds, dp->index, data + 4); |
91da11f8 LB |
1108 | } |
1109 | ||
6ca80638 | 1110 | static int dsa_user_get_sset_count(struct net_device *dev, int sset) |
91da11f8 | 1111 | { |
6ca80638 | 1112 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1113 | struct dsa_switch *ds = dp->ds; |
91da11f8 LB |
1114 | |
1115 | if (sset == ETH_SS_STATS) { | |
b94cbc90 | 1116 | int count = 0; |
91da11f8 | 1117 | |
b94cbc90 VO |
1118 | if (ds->ops->get_sset_count) { |
1119 | count = ds->ops->get_sset_count(ds, dp->index, sset); | |
1120 | if (count < 0) | |
1121 | return count; | |
1122 | } | |
91da11f8 | 1123 | |
b94cbc90 | 1124 | return count + 4; |
a71acad9 OR |
1125 | } else if (sset == ETH_SS_TEST) { |
1126 | return net_selftest_get_count(); | |
91da11f8 LB |
1127 | } |
1128 | ||
1129 | return -EOPNOTSUPP; | |
1130 | } | |
1131 | ||
6ca80638 FF |
1132 | static void dsa_user_get_eth_phy_stats(struct net_device *dev, |
1133 | struct ethtool_eth_phy_stats *phy_stats) | |
487d3855 | 1134 | { |
6ca80638 | 1135 | struct dsa_port *dp = dsa_user_to_port(dev); |
487d3855 AÅ |
1136 | struct dsa_switch *ds = dp->ds; |
1137 | ||
1138 | if (ds->ops->get_eth_phy_stats) | |
1139 | ds->ops->get_eth_phy_stats(ds, dp->index, phy_stats); | |
1140 | } | |
1141 | ||
6ca80638 FF |
1142 | static void dsa_user_get_eth_mac_stats(struct net_device *dev, |
1143 | struct ethtool_eth_mac_stats *mac_stats) | |
487d3855 | 1144 | { |
6ca80638 | 1145 | struct dsa_port *dp = dsa_user_to_port(dev); |
487d3855 AÅ |
1146 | struct dsa_switch *ds = dp->ds; |
1147 | ||
1148 | if (ds->ops->get_eth_mac_stats) | |
1149 | ds->ops->get_eth_mac_stats(ds, dp->index, mac_stats); | |
1150 | } | |
1151 | ||
1152 | static void | |
6ca80638 FF |
1153 | dsa_user_get_eth_ctrl_stats(struct net_device *dev, |
1154 | struct ethtool_eth_ctrl_stats *ctrl_stats) | |
487d3855 | 1155 | { |
6ca80638 | 1156 | struct dsa_port *dp = dsa_user_to_port(dev); |
487d3855 AÅ |
1157 | struct dsa_switch *ds = dp->ds; |
1158 | ||
1159 | if (ds->ops->get_eth_ctrl_stats) | |
1160 | ds->ops->get_eth_ctrl_stats(ds, dp->index, ctrl_stats); | |
1161 | } | |
1162 | ||
67f38b1c | 1163 | static void |
6ca80638 FF |
1164 | dsa_user_get_rmon_stats(struct net_device *dev, |
1165 | struct ethtool_rmon_stats *rmon_stats, | |
1166 | const struct ethtool_rmon_hist_range **ranges) | |
67f38b1c | 1167 | { |
6ca80638 | 1168 | struct dsa_port *dp = dsa_user_to_port(dev); |
67f38b1c CL |
1169 | struct dsa_switch *ds = dp->ds; |
1170 | ||
1171 | if (ds->ops->get_rmon_stats) | |
1172 | ds->ops->get_rmon_stats(ds, dp->index, rmon_stats, ranges); | |
1173 | } | |
1174 | ||
6ca80638 FF |
1175 | static void dsa_user_net_selftest(struct net_device *ndev, |
1176 | struct ethtool_test *etest, u64 *buf) | |
a71acad9 | 1177 | { |
6ca80638 | 1178 | struct dsa_port *dp = dsa_user_to_port(ndev); |
a71acad9 OR |
1179 | struct dsa_switch *ds = dp->ds; |
1180 | ||
1181 | if (ds->ops->self_test) { | |
1182 | ds->ops->self_test(ds, dp->index, etest, buf); | |
1183 | return; | |
1184 | } | |
1185 | ||
1186 | net_selftest(ndev, etest, buf); | |
1187 | } | |
1188 | ||
6ca80638 FF |
1189 | static int dsa_user_get_mm(struct net_device *dev, |
1190 | struct ethtool_mm_state *state) | |
5f6c2d49 | 1191 | { |
6ca80638 | 1192 | struct dsa_port *dp = dsa_user_to_port(dev); |
5f6c2d49 VO |
1193 | struct dsa_switch *ds = dp->ds; |
1194 | ||
1195 | if (!ds->ops->get_mm) | |
1196 | return -EOPNOTSUPP; | |
1197 | ||
1198 | return ds->ops->get_mm(ds, dp->index, state); | |
1199 | } | |
1200 | ||
6ca80638 FF |
1201 | static int dsa_user_set_mm(struct net_device *dev, struct ethtool_mm_cfg *cfg, |
1202 | struct netlink_ext_ack *extack) | |
5f6c2d49 | 1203 | { |
6ca80638 | 1204 | struct dsa_port *dp = dsa_user_to_port(dev); |
5f6c2d49 VO |
1205 | struct dsa_switch *ds = dp->ds; |
1206 | ||
1207 | if (!ds->ops->set_mm) | |
1208 | return -EOPNOTSUPP; | |
1209 | ||
1210 | return ds->ops->set_mm(ds, dp->index, cfg, extack); | |
1211 | } | |
1212 | ||
6ca80638 FF |
1213 | static void dsa_user_get_mm_stats(struct net_device *dev, |
1214 | struct ethtool_mm_stats *stats) | |
5f6c2d49 | 1215 | { |
6ca80638 | 1216 | struct dsa_port *dp = dsa_user_to_port(dev); |
5f6c2d49 VO |
1217 | struct dsa_switch *ds = dp->ds; |
1218 | ||
1219 | if (ds->ops->get_mm_stats) | |
1220 | ds->ops->get_mm_stats(ds, dp->index, stats); | |
1221 | } | |
1222 | ||
6ca80638 | 1223 | static void dsa_user_get_wol(struct net_device *dev, struct ethtool_wolinfo *w) |
19e57c4e | 1224 | { |
6ca80638 | 1225 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1226 | struct dsa_switch *ds = dp->ds; |
19e57c4e | 1227 | |
aab9c406 FF |
1228 | phylink_ethtool_get_wol(dp->pl, w); |
1229 | ||
9d490b4e | 1230 | if (ds->ops->get_wol) |
d945097b | 1231 | ds->ops->get_wol(ds, dp->index, w); |
19e57c4e FF |
1232 | } |
1233 | ||
6ca80638 | 1234 | static int dsa_user_set_wol(struct net_device *dev, struct ethtool_wolinfo *w) |
19e57c4e | 1235 | { |
6ca80638 | 1236 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1237 | struct dsa_switch *ds = dp->ds; |
19e57c4e FF |
1238 | int ret = -EOPNOTSUPP; |
1239 | ||
aab9c406 FF |
1240 | phylink_ethtool_set_wol(dp->pl, w); |
1241 | ||
9d490b4e | 1242 | if (ds->ops->set_wol) |
d945097b | 1243 | ret = ds->ops->set_wol(ds, dp->index, w); |
19e57c4e FF |
1244 | |
1245 | return ret; | |
1246 | } | |
1247 | ||
6ca80638 | 1248 | static int dsa_user_set_eee(struct net_device *dev, struct ethtool_eee *e) |
7905288f | 1249 | { |
6ca80638 | 1250 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1251 | struct dsa_switch *ds = dp->ds; |
7905288f FF |
1252 | int ret; |
1253 | ||
7b9cc738 | 1254 | /* Port's PHY and MAC both need to be EEE capable */ |
00670cb8 | 1255 | if (!dev->phydev || !dp->pl) |
7b9cc738 VD |
1256 | return -ENODEV; |
1257 | ||
08f50061 | 1258 | if (!ds->ops->set_mac_eee) |
7905288f FF |
1259 | return -EOPNOTSUPP; |
1260 | ||
d945097b | 1261 | ret = ds->ops->set_mac_eee(ds, dp->index, e); |
7905288f FF |
1262 | if (ret) |
1263 | return ret; | |
1264 | ||
aab9c406 | 1265 | return phylink_ethtool_set_eee(dp->pl, e); |
7905288f FF |
1266 | } |
1267 | ||
6ca80638 | 1268 | static int dsa_user_get_eee(struct net_device *dev, struct ethtool_eee *e) |
7905288f | 1269 | { |
6ca80638 | 1270 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1271 | struct dsa_switch *ds = dp->ds; |
7905288f FF |
1272 | int ret; |
1273 | ||
7b9cc738 | 1274 | /* Port's PHY and MAC both need to be EEE capable */ |
00670cb8 | 1275 | if (!dev->phydev || !dp->pl) |
7b9cc738 VD |
1276 | return -ENODEV; |
1277 | ||
08f50061 | 1278 | if (!ds->ops->get_mac_eee) |
7905288f FF |
1279 | return -EOPNOTSUPP; |
1280 | ||
d945097b | 1281 | ret = ds->ops->get_mac_eee(ds, dp->index, e); |
7905288f FF |
1282 | if (ret) |
1283 | return ret; | |
1284 | ||
aab9c406 FF |
1285 | return phylink_ethtool_get_eee(dp->pl, e); |
1286 | } | |
1287 | ||
6ca80638 FF |
1288 | static int dsa_user_get_link_ksettings(struct net_device *dev, |
1289 | struct ethtool_link_ksettings *cmd) | |
aab9c406 | 1290 | { |
6ca80638 | 1291 | struct dsa_port *dp = dsa_user_to_port(dev); |
aab9c406 FF |
1292 | |
1293 | return phylink_ethtool_ksettings_get(dp->pl, cmd); | |
1294 | } | |
1295 | ||
6ca80638 FF |
1296 | static int dsa_user_set_link_ksettings(struct net_device *dev, |
1297 | const struct ethtool_link_ksettings *cmd) | |
aab9c406 | 1298 | { |
6ca80638 | 1299 | struct dsa_port *dp = dsa_user_to_port(dev); |
aab9c406 FF |
1300 | |
1301 | return phylink_ethtool_ksettings_set(dp->pl, cmd); | |
7905288f FF |
1302 | } |
1303 | ||
6ca80638 FF |
1304 | static void dsa_user_get_pause_stats(struct net_device *dev, |
1305 | struct ethtool_pause_stats *pause_stats) | |
3d410403 | 1306 | { |
6ca80638 | 1307 | struct dsa_port *dp = dsa_user_to_port(dev); |
3d410403 OR |
1308 | struct dsa_switch *ds = dp->ds; |
1309 | ||
1310 | if (ds->ops->get_pause_stats) | |
1311 | ds->ops->get_pause_stats(ds, dp->index, pause_stats); | |
1312 | } | |
1313 | ||
6ca80638 FF |
1314 | static void dsa_user_get_pauseparam(struct net_device *dev, |
1315 | struct ethtool_pauseparam *pause) | |
a2a1a13b | 1316 | { |
6ca80638 | 1317 | struct dsa_port *dp = dsa_user_to_port(dev); |
a2a1a13b HK |
1318 | |
1319 | phylink_ethtool_get_pauseparam(dp->pl, pause); | |
1320 | } | |
1321 | ||
6ca80638 FF |
1322 | static int dsa_user_set_pauseparam(struct net_device *dev, |
1323 | struct ethtool_pauseparam *pause) | |
a2a1a13b | 1324 | { |
6ca80638 | 1325 | struct dsa_port *dp = dsa_user_to_port(dev); |
a2a1a13b HK |
1326 | |
1327 | return phylink_ethtool_set_pauseparam(dp->pl, pause); | |
1328 | } | |
1329 | ||
04ff53f9 | 1330 | #ifdef CONFIG_NET_POLL_CONTROLLER |
6ca80638 FF |
1331 | static int dsa_user_netpoll_setup(struct net_device *dev, |
1332 | struct netpoll_info *ni) | |
04ff53f9 | 1333 | { |
6ca80638 FF |
1334 | struct net_device *conduit = dsa_user_to_conduit(dev); |
1335 | struct dsa_user_priv *p = netdev_priv(dev); | |
04ff53f9 FF |
1336 | struct netpoll *netpoll; |
1337 | int err = 0; | |
1338 | ||
1339 | netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL); | |
1340 | if (!netpoll) | |
1341 | return -ENOMEM; | |
1342 | ||
6ca80638 | 1343 | err = __netpoll_setup(netpoll, conduit); |
04ff53f9 FF |
1344 | if (err) { |
1345 | kfree(netpoll); | |
1346 | goto out; | |
1347 | } | |
1348 | ||
1349 | p->netpoll = netpoll; | |
1350 | out: | |
1351 | return err; | |
1352 | } | |
1353 | ||
6ca80638 | 1354 | static void dsa_user_netpoll_cleanup(struct net_device *dev) |
04ff53f9 | 1355 | { |
6ca80638 | 1356 | struct dsa_user_priv *p = netdev_priv(dev); |
04ff53f9 FF |
1357 | struct netpoll *netpoll = p->netpoll; |
1358 | ||
1359 | if (!netpoll) | |
1360 | return; | |
1361 | ||
1362 | p->netpoll = NULL; | |
1363 | ||
c9fbd71f | 1364 | __netpoll_free(netpoll); |
04ff53f9 FF |
1365 | } |
1366 | ||
6ca80638 | 1367 | static void dsa_user_poll_controller(struct net_device *dev) |
04ff53f9 FF |
1368 | { |
1369 | } | |
1370 | #endif | |
1371 | ||
f50f2127 | 1372 | static struct dsa_mall_tc_entry * |
6ca80638 | 1373 | dsa_user_mall_tc_entry_find(struct net_device *dev, unsigned long cookie) |
f50f2127 | 1374 | { |
6ca80638 | 1375 | struct dsa_user_priv *p = netdev_priv(dev); |
f50f2127 FF |
1376 | struct dsa_mall_tc_entry *mall_tc_entry; |
1377 | ||
1378 | list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) | |
1379 | if (mall_tc_entry->cookie == cookie) | |
1380 | return mall_tc_entry; | |
1381 | ||
1382 | return NULL; | |
1383 | } | |
1384 | ||
e13c2075 | 1385 | static int |
6ca80638 FF |
1386 | dsa_user_add_cls_matchall_mirred(struct net_device *dev, |
1387 | struct tc_cls_matchall_offload *cls, | |
1388 | bool ingress) | |
f50f2127 | 1389 | { |
0148bb50 | 1390 | struct netlink_ext_ack *extack = cls->common.extack; |
6ca80638 FF |
1391 | struct dsa_port *dp = dsa_user_to_port(dev); |
1392 | struct dsa_user_priv *p = netdev_priv(dev); | |
e13c2075 | 1393 | struct dsa_mall_mirror_tc_entry *mirror; |
f50f2127 | 1394 | struct dsa_mall_tc_entry *mall_tc_entry; |
d945097b | 1395 | struct dsa_switch *ds = dp->ds; |
9681e8b3 | 1396 | struct flow_action_entry *act; |
d945097b | 1397 | struct dsa_port *to_dp; |
e13c2075 VO |
1398 | int err; |
1399 | ||
f50f2127 | 1400 | if (!ds->ops->port_mirror_add) |
34297176 | 1401 | return -EOPNOTSUPP; |
f50f2127 | 1402 | |
53eca1f3 JK |
1403 | if (!flow_action_basic_hw_stats_check(&cls->rule->action, |
1404 | cls->common.extack)) | |
34297176 | 1405 | return -EOPNOTSUPP; |
319a1d19 | 1406 | |
9681e8b3 | 1407 | act = &cls->rule->action.entries[0]; |
f50f2127 | 1408 | |
65722159 VO |
1409 | if (!act->dev) |
1410 | return -EINVAL; | |
1411 | ||
6ca80638 | 1412 | if (!dsa_user_dev_check(act->dev)) |
e13c2075 | 1413 | return -EOPNOTSUPP; |
f50f2127 | 1414 | |
e13c2075 VO |
1415 | mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); |
1416 | if (!mall_tc_entry) | |
1417 | return -ENOMEM; | |
f50f2127 | 1418 | |
e13c2075 VO |
1419 | mall_tc_entry->cookie = cls->cookie; |
1420 | mall_tc_entry->type = DSA_PORT_MALL_MIRROR; | |
1421 | mirror = &mall_tc_entry->mirror; | |
1422 | ||
6ca80638 | 1423 | to_dp = dsa_user_to_port(act->dev); |
f50f2127 | 1424 | |
e13c2075 VO |
1425 | mirror->to_local_port = to_dp->index; |
1426 | mirror->ingress = ingress; | |
f50f2127 | 1427 | |
0148bb50 | 1428 | err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress, extack); |
e13c2075 VO |
1429 | if (err) { |
1430 | kfree(mall_tc_entry); | |
1431 | return err; | |
1432 | } | |
f50f2127 | 1433 | |
e13c2075 | 1434 | list_add_tail(&mall_tc_entry->list, &p->mall_tc_list); |
f50f2127 | 1435 | |
e13c2075 VO |
1436 | return err; |
1437 | } | |
f50f2127 | 1438 | |
34297176 | 1439 | static int |
6ca80638 FF |
1440 | dsa_user_add_cls_matchall_police(struct net_device *dev, |
1441 | struct tc_cls_matchall_offload *cls, | |
1442 | bool ingress) | |
34297176 VO |
1443 | { |
1444 | struct netlink_ext_ack *extack = cls->common.extack; | |
6ca80638 FF |
1445 | struct dsa_port *dp = dsa_user_to_port(dev); |
1446 | struct dsa_user_priv *p = netdev_priv(dev); | |
34297176 VO |
1447 | struct dsa_mall_policer_tc_entry *policer; |
1448 | struct dsa_mall_tc_entry *mall_tc_entry; | |
1449 | struct dsa_switch *ds = dp->ds; | |
1450 | struct flow_action_entry *act; | |
1451 | int err; | |
1452 | ||
1453 | if (!ds->ops->port_policer_add) { | |
1454 | NL_SET_ERR_MSG_MOD(extack, | |
c75a33c8 | 1455 | "Policing offload not implemented"); |
34297176 VO |
1456 | return -EOPNOTSUPP; |
1457 | } | |
1458 | ||
1459 | if (!ingress) { | |
1460 | NL_SET_ERR_MSG_MOD(extack, | |
c75a33c8 | 1461 | "Only supported on ingress qdisc"); |
34297176 VO |
1462 | return -EOPNOTSUPP; |
1463 | } | |
1464 | ||
1465 | if (!flow_action_basic_hw_stats_check(&cls->rule->action, | |
1466 | cls->common.extack)) | |
1467 | return -EOPNOTSUPP; | |
1468 | ||
1469 | list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list) { | |
1470 | if (mall_tc_entry->type == DSA_PORT_MALL_POLICER) { | |
1471 | NL_SET_ERR_MSG_MOD(extack, | |
c75a33c8 | 1472 | "Only one port policer allowed"); |
34297176 VO |
1473 | return -EEXIST; |
1474 | } | |
1475 | } | |
1476 | ||
1477 | act = &cls->rule->action.entries[0]; | |
1478 | ||
1479 | mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); | |
1480 | if (!mall_tc_entry) | |
1481 | return -ENOMEM; | |
1482 | ||
1483 | mall_tc_entry->cookie = cls->cookie; | |
1484 | mall_tc_entry->type = DSA_PORT_MALL_POLICER; | |
1485 | policer = &mall_tc_entry->policer; | |
1486 | policer->rate_bytes_per_sec = act->police.rate_bytes_ps; | |
1487 | policer->burst = act->police.burst; | |
1488 | ||
1489 | err = ds->ops->port_policer_add(ds, dp->index, policer); | |
1490 | if (err) { | |
1491 | kfree(mall_tc_entry); | |
1492 | return err; | |
1493 | } | |
1494 | ||
1495 | list_add_tail(&mall_tc_entry->list, &p->mall_tc_list); | |
1496 | ||
1497 | return err; | |
1498 | } | |
1499 | ||
6ca80638 FF |
1500 | static int dsa_user_add_cls_matchall(struct net_device *dev, |
1501 | struct tc_cls_matchall_offload *cls, | |
1502 | bool ingress) | |
e13c2075 VO |
1503 | { |
1504 | int err = -EOPNOTSUPP; | |
f50f2127 | 1505 | |
e13c2075 VO |
1506 | if (cls->common.protocol == htons(ETH_P_ALL) && |
1507 | flow_offload_has_one_action(&cls->rule->action) && | |
1508 | cls->rule->action.entries[0].id == FLOW_ACTION_MIRRED) | |
6ca80638 | 1509 | err = dsa_user_add_cls_matchall_mirred(dev, cls, ingress); |
34297176 VO |
1510 | else if (flow_offload_has_one_action(&cls->rule->action) && |
1511 | cls->rule->action.entries[0].id == FLOW_ACTION_POLICE) | |
6ca80638 | 1512 | err = dsa_user_add_cls_matchall_police(dev, cls, ingress); |
f50f2127 | 1513 | |
e13c2075 | 1514 | return err; |
f50f2127 FF |
1515 | } |
1516 | ||
6ca80638 FF |
1517 | static void dsa_user_del_cls_matchall(struct net_device *dev, |
1518 | struct tc_cls_matchall_offload *cls) | |
f50f2127 | 1519 | { |
6ca80638 | 1520 | struct dsa_port *dp = dsa_user_to_port(dev); |
f50f2127 | 1521 | struct dsa_mall_tc_entry *mall_tc_entry; |
d945097b | 1522 | struct dsa_switch *ds = dp->ds; |
f50f2127 | 1523 | |
6ca80638 | 1524 | mall_tc_entry = dsa_user_mall_tc_entry_find(dev, cls->cookie); |
f50f2127 FF |
1525 | if (!mall_tc_entry) |
1526 | return; | |
1527 | ||
1528 | list_del(&mall_tc_entry->list); | |
1529 | ||
1530 | switch (mall_tc_entry->type) { | |
1531 | case DSA_PORT_MALL_MIRROR: | |
34297176 VO |
1532 | if (ds->ops->port_mirror_del) |
1533 | ds->ops->port_mirror_del(ds, dp->index, | |
1534 | &mall_tc_entry->mirror); | |
1535 | break; | |
1536 | case DSA_PORT_MALL_POLICER: | |
1537 | if (ds->ops->port_policer_del) | |
1538 | ds->ops->port_policer_del(ds, dp->index); | |
f50f2127 FF |
1539 | break; |
1540 | default: | |
1541 | WARN_ON(1); | |
1542 | } | |
1543 | ||
1544 | kfree(mall_tc_entry); | |
1545 | } | |
1546 | ||
6ca80638 FF |
1547 | static int dsa_user_setup_tc_cls_matchall(struct net_device *dev, |
1548 | struct tc_cls_matchall_offload *cls, | |
1549 | bool ingress) | |
f50f2127 | 1550 | { |
5fd9fc4e | 1551 | if (cls->common.chain_index) |
a5fcf8a6 | 1552 | return -EOPNOTSUPP; |
f50f2127 | 1553 | |
3fbae382 JP |
1554 | switch (cls->command) { |
1555 | case TC_CLSMATCHALL_REPLACE: | |
6ca80638 | 1556 | return dsa_user_add_cls_matchall(dev, cls, ingress); |
3fbae382 | 1557 | case TC_CLSMATCHALL_DESTROY: |
6ca80638 | 1558 | dsa_user_del_cls_matchall(dev, cls); |
3fbae382 JP |
1559 | return 0; |
1560 | default: | |
1561 | return -EOPNOTSUPP; | |
1562 | } | |
1563 | } | |
1564 | ||
6ca80638 FF |
1565 | static int dsa_user_add_cls_flower(struct net_device *dev, |
1566 | struct flow_cls_offload *cls, | |
1567 | bool ingress) | |
ed11bb1f | 1568 | { |
6ca80638 | 1569 | struct dsa_port *dp = dsa_user_to_port(dev); |
ed11bb1f VO |
1570 | struct dsa_switch *ds = dp->ds; |
1571 | int port = dp->index; | |
1572 | ||
1573 | if (!ds->ops->cls_flower_add) | |
1574 | return -EOPNOTSUPP; | |
1575 | ||
1576 | return ds->ops->cls_flower_add(ds, port, cls, ingress); | |
1577 | } | |
1578 | ||
6ca80638 FF |
1579 | static int dsa_user_del_cls_flower(struct net_device *dev, |
1580 | struct flow_cls_offload *cls, | |
1581 | bool ingress) | |
ed11bb1f | 1582 | { |
6ca80638 | 1583 | struct dsa_port *dp = dsa_user_to_port(dev); |
ed11bb1f VO |
1584 | struct dsa_switch *ds = dp->ds; |
1585 | int port = dp->index; | |
1586 | ||
1587 | if (!ds->ops->cls_flower_del) | |
1588 | return -EOPNOTSUPP; | |
1589 | ||
1590 | return ds->ops->cls_flower_del(ds, port, cls, ingress); | |
1591 | } | |
1592 | ||
6ca80638 FF |
1593 | static int dsa_user_stats_cls_flower(struct net_device *dev, |
1594 | struct flow_cls_offload *cls, | |
1595 | bool ingress) | |
ed11bb1f | 1596 | { |
6ca80638 | 1597 | struct dsa_port *dp = dsa_user_to_port(dev); |
ed11bb1f VO |
1598 | struct dsa_switch *ds = dp->ds; |
1599 | int port = dp->index; | |
1600 | ||
1601 | if (!ds->ops->cls_flower_stats) | |
1602 | return -EOPNOTSUPP; | |
1603 | ||
1604 | return ds->ops->cls_flower_stats(ds, port, cls, ingress); | |
1605 | } | |
1606 | ||
6ca80638 FF |
1607 | static int dsa_user_setup_tc_cls_flower(struct net_device *dev, |
1608 | struct flow_cls_offload *cls, | |
1609 | bool ingress) | |
ed11bb1f VO |
1610 | { |
1611 | switch (cls->command) { | |
1612 | case FLOW_CLS_REPLACE: | |
6ca80638 | 1613 | return dsa_user_add_cls_flower(dev, cls, ingress); |
ed11bb1f | 1614 | case FLOW_CLS_DESTROY: |
6ca80638 | 1615 | return dsa_user_del_cls_flower(dev, cls, ingress); |
ed11bb1f | 1616 | case FLOW_CLS_STATS: |
6ca80638 | 1617 | return dsa_user_stats_cls_flower(dev, cls, ingress); |
ed11bb1f VO |
1618 | default: |
1619 | return -EOPNOTSUPP; | |
1620 | } | |
1621 | } | |
1622 | ||
6ca80638 FF |
1623 | static int dsa_user_setup_tc_block_cb(enum tc_setup_type type, void *type_data, |
1624 | void *cb_priv, bool ingress) | |
6b3eb752 JP |
1625 | { |
1626 | struct net_device *dev = cb_priv; | |
1627 | ||
44ae12a7 JP |
1628 | if (!tc_can_offload(dev)) |
1629 | return -EOPNOTSUPP; | |
1630 | ||
6b3eb752 JP |
1631 | switch (type) { |
1632 | case TC_SETUP_CLSMATCHALL: | |
6ca80638 | 1633 | return dsa_user_setup_tc_cls_matchall(dev, type_data, ingress); |
ed11bb1f | 1634 | case TC_SETUP_CLSFLOWER: |
6ca80638 | 1635 | return dsa_user_setup_tc_cls_flower(dev, type_data, ingress); |
6b3eb752 JP |
1636 | default: |
1637 | return -EOPNOTSUPP; | |
1638 | } | |
1639 | } | |
1640 | ||
6ca80638 FF |
1641 | static int dsa_user_setup_tc_block_cb_ig(enum tc_setup_type type, |
1642 | void *type_data, void *cb_priv) | |
6b3eb752 | 1643 | { |
6ca80638 | 1644 | return dsa_user_setup_tc_block_cb(type, type_data, cb_priv, true); |
6b3eb752 JP |
1645 | } |
1646 | ||
6ca80638 FF |
1647 | static int dsa_user_setup_tc_block_cb_eg(enum tc_setup_type type, |
1648 | void *type_data, void *cb_priv) | |
6b3eb752 | 1649 | { |
6ca80638 | 1650 | return dsa_user_setup_tc_block_cb(type, type_data, cb_priv, false); |
6b3eb752 JP |
1651 | } |
1652 | ||
6ca80638 | 1653 | static LIST_HEAD(dsa_user_block_cb_list); |
955bcb6e | 1654 | |
6ca80638 FF |
1655 | static int dsa_user_setup_tc_block(struct net_device *dev, |
1656 | struct flow_block_offload *f) | |
6b3eb752 | 1657 | { |
955bcb6e | 1658 | struct flow_block_cb *block_cb; |
a7323311 | 1659 | flow_setup_cb_t *cb; |
6b3eb752 | 1660 | |
32f8c409 | 1661 | if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) |
6ca80638 | 1662 | cb = dsa_user_setup_tc_block_cb_ig; |
32f8c409 | 1663 | else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) |
6ca80638 | 1664 | cb = dsa_user_setup_tc_block_cb_eg; |
6b3eb752 JP |
1665 | else |
1666 | return -EOPNOTSUPP; | |
1667 | ||
6ca80638 | 1668 | f->driver_block_list = &dsa_user_block_cb_list; |
955bcb6e | 1669 | |
6b3eb752 | 1670 | switch (f->command) { |
9c0e189e | 1671 | case FLOW_BLOCK_BIND: |
6ca80638 | 1672 | if (flow_block_cb_is_busy(cb, dev, &dsa_user_block_cb_list)) |
0d4fd02e PNA |
1673 | return -EBUSY; |
1674 | ||
0c7294dd | 1675 | block_cb = flow_block_cb_alloc(cb, dev, dev, NULL); |
955bcb6e PNA |
1676 | if (IS_ERR(block_cb)) |
1677 | return PTR_ERR(block_cb); | |
1678 | ||
1679 | flow_block_cb_add(block_cb, f); | |
6ca80638 | 1680 | list_add_tail(&block_cb->driver_list, &dsa_user_block_cb_list); |
955bcb6e | 1681 | return 0; |
9c0e189e | 1682 | case FLOW_BLOCK_UNBIND: |
14bfb13f | 1683 | block_cb = flow_block_cb_lookup(f->block, cb, dev); |
955bcb6e PNA |
1684 | if (!block_cb) |
1685 | return -ENOENT; | |
1686 | ||
1687 | flow_block_cb_remove(block_cb, f); | |
1688 | list_del(&block_cb->driver_list); | |
6b3eb752 JP |
1689 | return 0; |
1690 | default: | |
1691 | return -EOPNOTSUPP; | |
1692 | } | |
1693 | } | |
1694 | ||
6ca80638 FF |
1695 | static int dsa_user_setup_ft_block(struct dsa_switch *ds, int port, |
1696 | void *type_data) | |
3fb24a43 | 1697 | { |
6ca80638 | 1698 | struct net_device *conduit = dsa_port_to_conduit(dsa_to_port(ds, port)); |
3fb24a43 | 1699 | |
6ca80638 | 1700 | if (!conduit->netdev_ops->ndo_setup_tc) |
3fb24a43 PNA |
1701 | return -EOPNOTSUPP; |
1702 | ||
6ca80638 | 1703 | return conduit->netdev_ops->ndo_setup_tc(conduit, TC_SETUP_FT, type_data); |
3fb24a43 PNA |
1704 | } |
1705 | ||
6ca80638 FF |
1706 | static int dsa_user_setup_tc(struct net_device *dev, enum tc_setup_type type, |
1707 | void *type_data) | |
3fbae382 | 1708 | { |
6ca80638 | 1709 | struct dsa_port *dp = dsa_user_to_port(dev); |
47d23af2 VO |
1710 | struct dsa_switch *ds = dp->ds; |
1711 | ||
3fb24a43 PNA |
1712 | switch (type) { |
1713 | case TC_SETUP_BLOCK: | |
6ca80638 | 1714 | return dsa_user_setup_tc_block(dev, type_data); |
3fb24a43 | 1715 | case TC_SETUP_FT: |
6ca80638 | 1716 | return dsa_user_setup_ft_block(ds, dp->index, type_data); |
3fb24a43 PNA |
1717 | default: |
1718 | break; | |
1719 | } | |
47d23af2 VO |
1720 | |
1721 | if (!ds->ops->port_setup_tc) | |
a5fcf8a6 | 1722 | return -EOPNOTSUPP; |
47d23af2 VO |
1723 | |
1724 | return ds->ops->port_setup_tc(ds, dp->index, type, type_data); | |
f50f2127 FF |
1725 | } |
1726 | ||
6ca80638 FF |
1727 | static int dsa_user_get_rxnfc(struct net_device *dev, |
1728 | struct ethtool_rxnfc *nfc, u32 *rule_locs) | |
bf9f2648 | 1729 | { |
6ca80638 | 1730 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1731 | struct dsa_switch *ds = dp->ds; |
bf9f2648 FF |
1732 | |
1733 | if (!ds->ops->get_rxnfc) | |
1734 | return -EOPNOTSUPP; | |
1735 | ||
d945097b | 1736 | return ds->ops->get_rxnfc(ds, dp->index, nfc, rule_locs); |
bf9f2648 FF |
1737 | } |
1738 | ||
6ca80638 FF |
1739 | static int dsa_user_set_rxnfc(struct net_device *dev, |
1740 | struct ethtool_rxnfc *nfc) | |
bf9f2648 | 1741 | { |
6ca80638 | 1742 | struct dsa_port *dp = dsa_user_to_port(dev); |
d945097b | 1743 | struct dsa_switch *ds = dp->ds; |
bf9f2648 FF |
1744 | |
1745 | if (!ds->ops->set_rxnfc) | |
1746 | return -EOPNOTSUPP; | |
1747 | ||
d945097b | 1748 | return ds->ops->set_rxnfc(ds, dp->index, nfc); |
bf9f2648 FF |
1749 | } |
1750 | ||
6ca80638 FF |
1751 | static int dsa_user_get_ts_info(struct net_device *dev, |
1752 | struct ethtool_ts_info *ts) | |
0336369d | 1753 | { |
6ca80638 | 1754 | struct dsa_user_priv *p = netdev_priv(dev); |
0336369d BS |
1755 | struct dsa_switch *ds = p->dp->ds; |
1756 | ||
1757 | if (!ds->ops->get_ts_info) | |
1758 | return -EOPNOTSUPP; | |
1759 | ||
1760 | return ds->ops->get_ts_info(ds, p->dp->index, ts); | |
1761 | } | |
1762 | ||
6ca80638 FF |
1763 | static int dsa_user_vlan_rx_add_vid(struct net_device *dev, __be16 proto, |
1764 | u16 vid) | |
061f6a50 | 1765 | { |
6ca80638 | 1766 | struct dsa_port *dp = dsa_user_to_port(dev); |
88236591 VO |
1767 | struct switchdev_obj_port_vlan vlan = { |
1768 | .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, | |
b7a9e0da | 1769 | .vid = vid, |
88236591 VO |
1770 | /* This API only allows programming tagged, non-PVID VIDs */ |
1771 | .flags = 0, | |
1772 | }; | |
31046a5f | 1773 | struct netlink_ext_ack extack = {0}; |
64fdc5f3 VO |
1774 | struct dsa_switch *ds = dp->ds; |
1775 | struct netdev_hw_addr *ha; | |
d06f925f | 1776 | struct dsa_vlan *v; |
061f6a50 FF |
1777 | int ret; |
1778 | ||
88236591 | 1779 | /* User port... */ |
31046a5f VO |
1780 | ret = dsa_port_vlan_add(dp, &vlan, &extack); |
1781 | if (ret) { | |
1782 | if (extack._msg) | |
1783 | netdev_err(dev, "%s\n", extack._msg); | |
cf360866 | 1784 | return ret; |
31046a5f | 1785 | } |
cf360866 | 1786 | |
88236591 | 1787 | /* And CPU port... */ |
134ef238 | 1788 | ret = dsa_port_host_vlan_add(dp, &vlan, &extack); |
31046a5f VO |
1789 | if (ret) { |
1790 | if (extack._msg) | |
1791 | netdev_err(dev, "CPU port %d: %s\n", dp->cpu_dp->index, | |
1792 | extack._msg); | |
7e1741b4 | 1793 | return ret; |
31046a5f | 1794 | } |
7e1741b4 | 1795 | |
64fdc5f3 VO |
1796 | if (!dsa_switch_supports_uc_filtering(ds) && |
1797 | !dsa_switch_supports_mc_filtering(ds)) | |
1798 | return 0; | |
1799 | ||
d06f925f VO |
1800 | v = kzalloc(sizeof(*v), GFP_KERNEL); |
1801 | if (!v) { | |
1802 | ret = -ENOMEM; | |
1803 | goto rollback; | |
1804 | } | |
1805 | ||
64fdc5f3 VO |
1806 | netif_addr_lock_bh(dev); |
1807 | ||
d06f925f VO |
1808 | v->vid = vid; |
1809 | list_add_tail(&v->list, &dp->user_vlans); | |
1810 | ||
64fdc5f3 VO |
1811 | if (dsa_switch_supports_mc_filtering(ds)) { |
1812 | netdev_for_each_synced_mc_addr(ha, dev) { | |
6ca80638 FF |
1813 | dsa_user_schedule_standalone_work(dev, DSA_MC_ADD, |
1814 | ha->addr, vid); | |
64fdc5f3 VO |
1815 | } |
1816 | } | |
1817 | ||
1818 | if (dsa_switch_supports_uc_filtering(ds)) { | |
1819 | netdev_for_each_synced_uc_addr(ha, dev) { | |
6ca80638 FF |
1820 | dsa_user_schedule_standalone_work(dev, DSA_UC_ADD, |
1821 | ha->addr, vid); | |
64fdc5f3 VO |
1822 | } |
1823 | } | |
1824 | ||
1825 | netif_addr_unlock_bh(dev); | |
1826 | ||
1827 | dsa_flush_workqueue(); | |
1828 | ||
134ef238 | 1829 | return 0; |
d06f925f VO |
1830 | |
1831 | rollback: | |
1832 | dsa_port_host_vlan_del(dp, &vlan); | |
1833 | dsa_port_vlan_del(dp, &vlan); | |
1834 | ||
1835 | return ret; | |
061f6a50 FF |
1836 | } |
1837 | ||
6ca80638 FF |
1838 | static int dsa_user_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, |
1839 | u16 vid) | |
061f6a50 | 1840 | { |
6ca80638 | 1841 | struct dsa_port *dp = dsa_user_to_port(dev); |
88236591 | 1842 | struct switchdev_obj_port_vlan vlan = { |
b7a9e0da | 1843 | .vid = vid, |
88236591 VO |
1844 | /* This API only allows programming tagged, non-PVID VIDs */ |
1845 | .flags = 0, | |
1846 | }; | |
64fdc5f3 VO |
1847 | struct dsa_switch *ds = dp->ds; |
1848 | struct netdev_hw_addr *ha; | |
d06f925f | 1849 | struct dsa_vlan *v; |
2209158c | 1850 | int err; |
061f6a50 | 1851 | |
2209158c VO |
1852 | err = dsa_port_vlan_del(dp, &vlan); |
1853 | if (err) | |
1854 | return err; | |
1855 | ||
64fdc5f3 VO |
1856 | err = dsa_port_host_vlan_del(dp, &vlan); |
1857 | if (err) | |
1858 | return err; | |
1859 | ||
1860 | if (!dsa_switch_supports_uc_filtering(ds) && | |
1861 | !dsa_switch_supports_mc_filtering(ds)) | |
1862 | return 0; | |
1863 | ||
1864 | netif_addr_lock_bh(dev); | |
1865 | ||
d06f925f VO |
1866 | v = dsa_vlan_find(&dp->user_vlans, &vlan); |
1867 | if (!v) { | |
1868 | netif_addr_unlock_bh(dev); | |
1869 | return -ENOENT; | |
1870 | } | |
1871 | ||
1872 | list_del(&v->list); | |
1873 | kfree(v); | |
1874 | ||
64fdc5f3 VO |
1875 | if (dsa_switch_supports_mc_filtering(ds)) { |
1876 | netdev_for_each_synced_mc_addr(ha, dev) { | |
6ca80638 FF |
1877 | dsa_user_schedule_standalone_work(dev, DSA_MC_DEL, |
1878 | ha->addr, vid); | |
64fdc5f3 VO |
1879 | } |
1880 | } | |
1881 | ||
1882 | if (dsa_switch_supports_uc_filtering(ds)) { | |
1883 | netdev_for_each_synced_uc_addr(ha, dev) { | |
6ca80638 FF |
1884 | dsa_user_schedule_standalone_work(dev, DSA_UC_DEL, |
1885 | ha->addr, vid); | |
64fdc5f3 VO |
1886 | } |
1887 | } | |
1888 | ||
1889 | netif_addr_unlock_bh(dev); | |
1890 | ||
1891 | dsa_flush_workqueue(); | |
1892 | ||
1893 | return 0; | |
061f6a50 FF |
1894 | } |
1895 | ||
6ca80638 | 1896 | static int dsa_user_restore_vlan(struct net_device *vdev, int vid, void *arg) |
06cfb2df VO |
1897 | { |
1898 | __be16 proto = vdev ? vlan_dev_vlan_proto(vdev) : htons(ETH_P_8021Q); | |
1899 | ||
6ca80638 | 1900 | return dsa_user_vlan_rx_add_vid(arg, proto, vid); |
06cfb2df VO |
1901 | } |
1902 | ||
6ca80638 | 1903 | static int dsa_user_clear_vlan(struct net_device *vdev, int vid, void *arg) |
06cfb2df VO |
1904 | { |
1905 | __be16 proto = vdev ? vlan_dev_vlan_proto(vdev) : htons(ETH_P_8021Q); | |
1906 | ||
6ca80638 | 1907 | return dsa_user_vlan_rx_kill_vid(arg, proto, vid); |
06cfb2df VO |
1908 | } |
1909 | ||
1910 | /* Keep the VLAN RX filtering list in sync with the hardware only if VLAN | |
1911 | * filtering is enabled. The baseline is that only ports that offload a | |
1912 | * VLAN-aware bridge are VLAN-aware, and standalone ports are VLAN-unaware, | |
1913 | * but there are exceptions for quirky hardware. | |
1914 | * | |
1915 | * If ds->vlan_filtering_is_global = true, then standalone ports which share | |
1916 | * the same switch with other ports that offload a VLAN-aware bridge are also | |
1917 | * inevitably VLAN-aware. | |
1918 | * | |
1919 | * To summarize, a DSA switch port offloads: | |
1920 | * | |
1921 | * - If standalone (this includes software bridge, software LAG): | |
58adf9dc VO |
1922 | * - if ds->needs_standalone_vlan_filtering = true, OR if |
1923 | * (ds->vlan_filtering_is_global = true AND there are bridges spanning | |
1924 | * this switch chip which have vlan_filtering=1) | |
06cfb2df | 1925 | * - the 8021q upper VLANs |
58adf9dc VO |
1926 | * - else (standalone VLAN filtering is not needed, VLAN filtering is not |
1927 | * global, or it is, but no port is under a VLAN-aware bridge): | |
06cfb2df VO |
1928 | * - no VLAN (any 8021q upper is a software VLAN) |
1929 | * | |
1930 | * - If under a vlan_filtering=0 bridge which it offload: | |
1931 | * - if ds->configure_vlan_while_not_filtering = true (default): | |
1932 | * - the bridge VLANs. These VLANs are committed to hardware but inactive. | |
1933 | * - else (deprecated): | |
1934 | * - no VLAN. The bridge VLANs are not restored when VLAN awareness is | |
1935 | * enabled, so this behavior is broken and discouraged. | |
1936 | * | |
1937 | * - If under a vlan_filtering=1 bridge which it offload: | |
1938 | * - the bridge VLANs | |
1939 | * - the 8021q upper VLANs | |
1940 | */ | |
6ca80638 FF |
1941 | int dsa_user_manage_vlan_filtering(struct net_device *user, |
1942 | bool vlan_filtering) | |
06cfb2df VO |
1943 | { |
1944 | int err; | |
1945 | ||
1946 | if (vlan_filtering) { | |
6ca80638 | 1947 | user->features |= NETIF_F_HW_VLAN_CTAG_FILTER; |
06cfb2df | 1948 | |
6ca80638 | 1949 | err = vlan_for_each(user, dsa_user_restore_vlan, user); |
06cfb2df | 1950 | if (err) { |
6ca80638 FF |
1951 | vlan_for_each(user, dsa_user_clear_vlan, user); |
1952 | user->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; | |
06cfb2df VO |
1953 | return err; |
1954 | } | |
1955 | } else { | |
6ca80638 | 1956 | err = vlan_for_each(user, dsa_user_clear_vlan, user); |
06cfb2df VO |
1957 | if (err) |
1958 | return err; | |
1959 | ||
6ca80638 | 1960 | user->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; |
06cfb2df VO |
1961 | } |
1962 | ||
1963 | return 0; | |
1964 | } | |
1965 | ||
bff33f7e VO |
1966 | struct dsa_hw_port { |
1967 | struct list_head list; | |
1968 | struct net_device *dev; | |
1969 | int old_mtu; | |
1970 | }; | |
1971 | ||
1972 | static int dsa_hw_port_list_set_mtu(struct list_head *hw_port_list, int mtu) | |
1973 | { | |
1974 | const struct dsa_hw_port *p; | |
1975 | int err; | |
1976 | ||
1977 | list_for_each_entry(p, hw_port_list, list) { | |
1978 | if (p->dev->mtu == mtu) | |
1979 | continue; | |
1980 | ||
1981 | err = dev_set_mtu(p->dev, mtu); | |
1982 | if (err) | |
1983 | goto rollback; | |
1984 | } | |
1985 | ||
1986 | return 0; | |
1987 | ||
1988 | rollback: | |
1989 | list_for_each_entry_continue_reverse(p, hw_port_list, list) { | |
1990 | if (p->dev->mtu == p->old_mtu) | |
1991 | continue; | |
1992 | ||
1993 | if (dev_set_mtu(p->dev, p->old_mtu)) | |
1994 | netdev_err(p->dev, "Failed to restore MTU\n"); | |
1995 | } | |
1996 | ||
1997 | return err; | |
1998 | } | |
1999 | ||
2000 | static void dsa_hw_port_list_free(struct list_head *hw_port_list) | |
2001 | { | |
2002 | struct dsa_hw_port *p, *n; | |
2003 | ||
2004 | list_for_each_entry_safe(p, n, hw_port_list, list) | |
2005 | kfree(p); | |
2006 | } | |
2007 | ||
2008 | /* Make the hardware datapath to/from @dev limited to a common MTU */ | |
bf88dc32 | 2009 | static void dsa_bridge_mtu_normalization(struct dsa_port *dp) |
bff33f7e VO |
2010 | { |
2011 | struct list_head hw_port_list; | |
2012 | struct dsa_switch_tree *dst; | |
2013 | int min_mtu = ETH_MAX_MTU; | |
2014 | struct dsa_port *other_dp; | |
2015 | int err; | |
2016 | ||
2017 | if (!dp->ds->mtu_enforcement_ingress) | |
2018 | return; | |
2019 | ||
d3eed0e5 | 2020 | if (!dp->bridge) |
bff33f7e VO |
2021 | return; |
2022 | ||
2023 | INIT_LIST_HEAD(&hw_port_list); | |
2024 | ||
2025 | /* Populate the list of ports that are part of the same bridge | |
2026 | * as the newly added/modified port | |
2027 | */ | |
2028 | list_for_each_entry(dst, &dsa_tree_list, list) { | |
2029 | list_for_each_entry(other_dp, &dst->ports, list) { | |
2030 | struct dsa_hw_port *hw_port; | |
6ca80638 | 2031 | struct net_device *user; |
bff33f7e VO |
2032 | |
2033 | if (other_dp->type != DSA_PORT_TYPE_USER) | |
2034 | continue; | |
2035 | ||
36cbf39b | 2036 | if (!dsa_port_bridge_same(dp, other_dp)) |
bff33f7e VO |
2037 | continue; |
2038 | ||
2039 | if (!other_dp->ds->mtu_enforcement_ingress) | |
2040 | continue; | |
2041 | ||
6ca80638 | 2042 | user = other_dp->user; |
bff33f7e | 2043 | |
6ca80638 FF |
2044 | if (min_mtu > user->mtu) |
2045 | min_mtu = user->mtu; | |
bff33f7e VO |
2046 | |
2047 | hw_port = kzalloc(sizeof(*hw_port), GFP_KERNEL); | |
2048 | if (!hw_port) | |
2049 | goto out; | |
2050 | ||
6ca80638 FF |
2051 | hw_port->dev = user; |
2052 | hw_port->old_mtu = user->mtu; | |
bff33f7e VO |
2053 | |
2054 | list_add(&hw_port->list, &hw_port_list); | |
2055 | } | |
2056 | } | |
2057 | ||
2058 | /* Attempt to configure the entire hardware bridge to the newly added | |
2059 | * interface's MTU first, regardless of whether the intention of the | |
2060 | * user was to raise or lower it. | |
2061 | */ | |
6ca80638 | 2062 | err = dsa_hw_port_list_set_mtu(&hw_port_list, dp->user->mtu); |
bff33f7e VO |
2063 | if (!err) |
2064 | goto out; | |
2065 | ||
2066 | /* Clearly that didn't work out so well, so just set the minimum MTU on | |
2067 | * all hardware bridge ports now. If this fails too, then all ports will | |
2068 | * still have their old MTU rolled back anyway. | |
2069 | */ | |
2070 | dsa_hw_port_list_set_mtu(&hw_port_list, min_mtu); | |
2071 | ||
2072 | out: | |
2073 | dsa_hw_port_list_free(&hw_port_list); | |
2074 | } | |
2075 | ||
6ca80638 | 2076 | int dsa_user_change_mtu(struct net_device *dev, int new_mtu) |
bfcb8132 | 2077 | { |
6ca80638 FF |
2078 | struct net_device *conduit = dsa_user_to_conduit(dev); |
2079 | struct dsa_port *dp = dsa_user_to_port(dev); | |
cf1c39d3 | 2080 | struct dsa_port *cpu_dp = dp->cpu_dp; |
4715029f | 2081 | struct dsa_switch *ds = dp->ds; |
b2033a05 | 2082 | struct dsa_port *other_dp; |
bfcb8132 | 2083 | int largest_mtu = 0; |
6ca80638 FF |
2084 | int new_conduit_mtu; |
2085 | int old_conduit_mtu; | |
bfcb8132 | 2086 | int mtu_limit; |
636e8adf | 2087 | int overhead; |
bfcb8132 | 2088 | int cpu_mtu; |
4e4ab795 | 2089 | int err; |
bfcb8132 VO |
2090 | |
2091 | if (!ds->ops->port_change_mtu) | |
2092 | return -EOPNOTSUPP; | |
2093 | ||
b2033a05 | 2094 | dsa_tree_for_each_user_port(other_dp, ds->dst) { |
6ca80638 | 2095 | int user_mtu; |
bfcb8132 | 2096 | |
6ca80638 | 2097 | /* During probe, this function will be called for each user |
bfcb8132 VO |
2098 | * device, while not all of them have been allocated. That's |
2099 | * ok, it doesn't change what the maximum is, so ignore it. | |
2100 | */ | |
6ca80638 | 2101 | if (!other_dp->user) |
bfcb8132 VO |
2102 | continue; |
2103 | ||
2104 | /* Pretend that we already applied the setting, which we | |
2105 | * actually haven't (still haven't done all integrity checks) | |
2106 | */ | |
b2033a05 | 2107 | if (dp == other_dp) |
6ca80638 | 2108 | user_mtu = new_mtu; |
bfcb8132 | 2109 | else |
6ca80638 | 2110 | user_mtu = other_dp->user->mtu; |
bfcb8132 | 2111 | |
6ca80638 FF |
2112 | if (largest_mtu < user_mtu) |
2113 | largest_mtu = user_mtu; | |
bfcb8132 VO |
2114 | } |
2115 | ||
636e8adf | 2116 | overhead = dsa_tag_protocol_overhead(cpu_dp->tag_ops); |
6ca80638 FF |
2117 | mtu_limit = min_t(int, conduit->max_mtu, dev->max_mtu + overhead); |
2118 | old_conduit_mtu = conduit->mtu; | |
2119 | new_conduit_mtu = largest_mtu + overhead; | |
2120 | if (new_conduit_mtu > mtu_limit) | |
bfcb8132 VO |
2121 | return -ERANGE; |
2122 | ||
6ca80638 | 2123 | /* If the conduit MTU isn't over limit, there's no need to check the CPU |
bfcb8132 VO |
2124 | * MTU, since that surely isn't either. |
2125 | */ | |
2126 | cpu_mtu = largest_mtu; | |
2127 | ||
2128 | /* Start applying stuff */ | |
6ca80638 FF |
2129 | if (new_conduit_mtu != old_conduit_mtu) { |
2130 | err = dev_set_mtu(conduit, new_conduit_mtu); | |
bfcb8132 | 2131 | if (err < 0) |
6ca80638 | 2132 | goto out_conduit_failed; |
bfcb8132 VO |
2133 | |
2134 | /* We only need to propagate the MTU of the CPU port to | |
be6ff966 | 2135 | * upstream switches, so emit a notifier which updates them. |
bfcb8132 | 2136 | */ |
be6ff966 | 2137 | err = dsa_port_mtu_change(cpu_dp, cpu_mtu); |
bfcb8132 VO |
2138 | if (err) |
2139 | goto out_cpu_failed; | |
2140 | } | |
2141 | ||
be6ff966 | 2142 | err = ds->ops->port_change_mtu(ds, dp->index, new_mtu); |
bfcb8132 VO |
2143 | if (err) |
2144 | goto out_port_failed; | |
2145 | ||
2146 | dev->mtu = new_mtu; | |
2147 | ||
bff33f7e VO |
2148 | dsa_bridge_mtu_normalization(dp); |
2149 | ||
bfcb8132 VO |
2150 | return 0; |
2151 | ||
2152 | out_port_failed: | |
6ca80638 FF |
2153 | if (new_conduit_mtu != old_conduit_mtu) |
2154 | dsa_port_mtu_change(cpu_dp, old_conduit_mtu - overhead); | |
bfcb8132 | 2155 | out_cpu_failed: |
6ca80638 FF |
2156 | if (new_conduit_mtu != old_conduit_mtu) |
2157 | dev_set_mtu(conduit, old_conduit_mtu); | |
2158 | out_conduit_failed: | |
bfcb8132 VO |
2159 | return err; |
2160 | } | |
2161 | ||
d538eca8 | 2162 | static int __maybe_unused |
6ca80638 | 2163 | dsa_user_dcbnl_set_default_prio(struct net_device *dev, struct dcb_app *app) |
d538eca8 | 2164 | { |
6ca80638 | 2165 | struct dsa_port *dp = dsa_user_to_port(dev); |
d538eca8 VO |
2166 | struct dsa_switch *ds = dp->ds; |
2167 | unsigned long mask, new_prio; | |
2168 | int err, port = dp->index; | |
2169 | ||
2170 | if (!ds->ops->port_set_default_prio) | |
2171 | return -EOPNOTSUPP; | |
2172 | ||
2173 | err = dcb_ieee_setapp(dev, app); | |
2174 | if (err) | |
2175 | return err; | |
2176 | ||
2177 | mask = dcb_ieee_getapp_mask(dev, app); | |
2178 | new_prio = __fls(mask); | |
2179 | ||
2180 | err = ds->ops->port_set_default_prio(ds, port, new_prio); | |
2181 | if (err) { | |
2182 | dcb_ieee_delapp(dev, app); | |
2183 | return err; | |
2184 | } | |
2185 | ||
2186 | return 0; | |
2187 | } | |
2188 | ||
47d75f78 | 2189 | static int __maybe_unused |
6ca80638 | 2190 | dsa_user_dcbnl_add_dscp_prio(struct net_device *dev, struct dcb_app *app) |
47d75f78 | 2191 | { |
6ca80638 | 2192 | struct dsa_port *dp = dsa_user_to_port(dev); |
47d75f78 VO |
2193 | struct dsa_switch *ds = dp->ds; |
2194 | unsigned long mask, new_prio; | |
2195 | int err, port = dp->index; | |
2196 | u8 dscp = app->protocol; | |
2197 | ||
2198 | if (!ds->ops->port_add_dscp_prio) | |
2199 | return -EOPNOTSUPP; | |
2200 | ||
2201 | if (dscp >= 64) { | |
2202 | netdev_err(dev, "DSCP APP entry with protocol value %u is invalid\n", | |
2203 | dscp); | |
2204 | return -EINVAL; | |
2205 | } | |
2206 | ||
2207 | err = dcb_ieee_setapp(dev, app); | |
2208 | if (err) | |
2209 | return err; | |
2210 | ||
2211 | mask = dcb_ieee_getapp_mask(dev, app); | |
2212 | new_prio = __fls(mask); | |
2213 | ||
2214 | err = ds->ops->port_add_dscp_prio(ds, port, dscp, new_prio); | |
2215 | if (err) { | |
2216 | dcb_ieee_delapp(dev, app); | |
2217 | return err; | |
2218 | } | |
2219 | ||
2220 | return 0; | |
2221 | } | |
2222 | ||
6ca80638 FF |
2223 | static int __maybe_unused dsa_user_dcbnl_ieee_setapp(struct net_device *dev, |
2224 | struct dcb_app *app) | |
d538eca8 VO |
2225 | { |
2226 | switch (app->selector) { | |
2227 | case IEEE_8021QAZ_APP_SEL_ETHERTYPE: | |
2228 | switch (app->protocol) { | |
2229 | case 0: | |
6ca80638 | 2230 | return dsa_user_dcbnl_set_default_prio(dev, app); |
d538eca8 VO |
2231 | default: |
2232 | return -EOPNOTSUPP; | |
2233 | } | |
2234 | break; | |
47d75f78 | 2235 | case IEEE_8021QAZ_APP_SEL_DSCP: |
6ca80638 | 2236 | return dsa_user_dcbnl_add_dscp_prio(dev, app); |
d538eca8 VO |
2237 | default: |
2238 | return -EOPNOTSUPP; | |
2239 | } | |
2240 | } | |
2241 | ||
2242 | static int __maybe_unused | |
6ca80638 | 2243 | dsa_user_dcbnl_del_default_prio(struct net_device *dev, struct dcb_app *app) |
d538eca8 | 2244 | { |
6ca80638 | 2245 | struct dsa_port *dp = dsa_user_to_port(dev); |
d538eca8 VO |
2246 | struct dsa_switch *ds = dp->ds; |
2247 | unsigned long mask, new_prio; | |
2248 | int err, port = dp->index; | |
2249 | ||
2250 | if (!ds->ops->port_set_default_prio) | |
2251 | return -EOPNOTSUPP; | |
2252 | ||
2253 | err = dcb_ieee_delapp(dev, app); | |
2254 | if (err) | |
2255 | return err; | |
2256 | ||
2257 | mask = dcb_ieee_getapp_mask(dev, app); | |
2258 | new_prio = mask ? __fls(mask) : 0; | |
2259 | ||
2260 | err = ds->ops->port_set_default_prio(ds, port, new_prio); | |
2261 | if (err) { | |
2262 | dcb_ieee_setapp(dev, app); | |
2263 | return err; | |
2264 | } | |
2265 | ||
2266 | return 0; | |
2267 | } | |
2268 | ||
47d75f78 | 2269 | static int __maybe_unused |
6ca80638 | 2270 | dsa_user_dcbnl_del_dscp_prio(struct net_device *dev, struct dcb_app *app) |
47d75f78 | 2271 | { |
6ca80638 | 2272 | struct dsa_port *dp = dsa_user_to_port(dev); |
47d75f78 VO |
2273 | struct dsa_switch *ds = dp->ds; |
2274 | int err, port = dp->index; | |
2275 | u8 dscp = app->protocol; | |
2276 | ||
2277 | if (!ds->ops->port_del_dscp_prio) | |
2278 | return -EOPNOTSUPP; | |
2279 | ||
2280 | err = dcb_ieee_delapp(dev, app); | |
2281 | if (err) | |
2282 | return err; | |
2283 | ||
2284 | err = ds->ops->port_del_dscp_prio(ds, port, dscp, app->priority); | |
2285 | if (err) { | |
2286 | dcb_ieee_setapp(dev, app); | |
2287 | return err; | |
2288 | } | |
2289 | ||
2290 | return 0; | |
2291 | } | |
2292 | ||
6ca80638 FF |
2293 | static int __maybe_unused dsa_user_dcbnl_ieee_delapp(struct net_device *dev, |
2294 | struct dcb_app *app) | |
d538eca8 VO |
2295 | { |
2296 | switch (app->selector) { | |
2297 | case IEEE_8021QAZ_APP_SEL_ETHERTYPE: | |
2298 | switch (app->protocol) { | |
2299 | case 0: | |
6ca80638 | 2300 | return dsa_user_dcbnl_del_default_prio(dev, app); |
d538eca8 VO |
2301 | default: |
2302 | return -EOPNOTSUPP; | |
2303 | } | |
2304 | break; | |
47d75f78 | 2305 | case IEEE_8021QAZ_APP_SEL_DSCP: |
6ca80638 | 2306 | return dsa_user_dcbnl_del_dscp_prio(dev, app); |
d538eca8 VO |
2307 | default: |
2308 | return -EOPNOTSUPP; | |
2309 | } | |
2310 | } | |
2311 | ||
2312 | /* Pre-populate the DCB application priority table with the priorities | |
2313 | * configured during switch setup, which we read from hardware here. | |
2314 | */ | |
6ca80638 | 2315 | static int dsa_user_dcbnl_init(struct net_device *dev) |
d538eca8 | 2316 | { |
6ca80638 | 2317 | struct dsa_port *dp = dsa_user_to_port(dev); |
d538eca8 VO |
2318 | struct dsa_switch *ds = dp->ds; |
2319 | int port = dp->index; | |
2320 | int err; | |
2321 | ||
2322 | if (ds->ops->port_get_default_prio) { | |
2323 | int prio = ds->ops->port_get_default_prio(ds, port); | |
2324 | struct dcb_app app = { | |
2325 | .selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE, | |
2326 | .protocol = 0, | |
2327 | .priority = prio, | |
2328 | }; | |
2329 | ||
2330 | if (prio < 0) | |
2331 | return prio; | |
2332 | ||
2333 | err = dcb_ieee_setapp(dev, &app); | |
2334 | if (err) | |
2335 | return err; | |
2336 | } | |
2337 | ||
47d75f78 VO |
2338 | if (ds->ops->port_get_dscp_prio) { |
2339 | int protocol; | |
2340 | ||
2341 | for (protocol = 0; protocol < 64; protocol++) { | |
2342 | struct dcb_app app = { | |
2343 | .selector = IEEE_8021QAZ_APP_SEL_DSCP, | |
2344 | .protocol = protocol, | |
2345 | }; | |
2346 | int prio; | |
2347 | ||
2348 | prio = ds->ops->port_get_dscp_prio(ds, port, protocol); | |
2349 | if (prio == -EOPNOTSUPP) | |
2350 | continue; | |
2351 | if (prio < 0) | |
2352 | return prio; | |
2353 | ||
2354 | app.priority = prio; | |
2355 | ||
2356 | err = dcb_ieee_setapp(dev, &app); | |
2357 | if (err) | |
2358 | return err; | |
2359 | } | |
2360 | } | |
2361 | ||
d538eca8 VO |
2362 | return 0; |
2363 | } | |
2364 | ||
6ca80638 FF |
2365 | static const struct ethtool_ops dsa_user_ethtool_ops = { |
2366 | .get_drvinfo = dsa_user_get_drvinfo, | |
2367 | .get_regs_len = dsa_user_get_regs_len, | |
2368 | .get_regs = dsa_user_get_regs, | |
2369 | .nway_reset = dsa_user_nway_reset, | |
c4aef9fc | 2370 | .get_link = ethtool_op_get_link, |
6ca80638 FF |
2371 | .get_eeprom_len = dsa_user_get_eeprom_len, |
2372 | .get_eeprom = dsa_user_get_eeprom, | |
2373 | .set_eeprom = dsa_user_set_eeprom, | |
2374 | .get_strings = dsa_user_get_strings, | |
2375 | .get_ethtool_stats = dsa_user_get_ethtool_stats, | |
2376 | .get_sset_count = dsa_user_get_sset_count, | |
2377 | .get_eth_phy_stats = dsa_user_get_eth_phy_stats, | |
2378 | .get_eth_mac_stats = dsa_user_get_eth_mac_stats, | |
2379 | .get_eth_ctrl_stats = dsa_user_get_eth_ctrl_stats, | |
2380 | .get_rmon_stats = dsa_user_get_rmon_stats, | |
2381 | .set_wol = dsa_user_set_wol, | |
2382 | .get_wol = dsa_user_get_wol, | |
2383 | .set_eee = dsa_user_set_eee, | |
2384 | .get_eee = dsa_user_get_eee, | |
2385 | .get_link_ksettings = dsa_user_get_link_ksettings, | |
2386 | .set_link_ksettings = dsa_user_set_link_ksettings, | |
2387 | .get_pause_stats = dsa_user_get_pause_stats, | |
2388 | .get_pauseparam = dsa_user_get_pauseparam, | |
2389 | .set_pauseparam = dsa_user_set_pauseparam, | |
2390 | .get_rxnfc = dsa_user_get_rxnfc, | |
2391 | .set_rxnfc = dsa_user_set_rxnfc, | |
2392 | .get_ts_info = dsa_user_get_ts_info, | |
2393 | .self_test = dsa_user_net_selftest, | |
2394 | .get_mm = dsa_user_get_mm, | |
2395 | .set_mm = dsa_user_set_mm, | |
2396 | .get_mm_stats = dsa_user_get_mm_stats, | |
91da11f8 LB |
2397 | }; |
2398 | ||
6ca80638 FF |
2399 | static const struct dcbnl_rtnl_ops __maybe_unused dsa_user_dcbnl_ops = { |
2400 | .ieee_setapp = dsa_user_dcbnl_ieee_setapp, | |
2401 | .ieee_delapp = dsa_user_dcbnl_ieee_delapp, | |
d538eca8 VO |
2402 | }; |
2403 | ||
6ca80638 FF |
2404 | static void dsa_user_get_stats64(struct net_device *dev, |
2405 | struct rtnl_link_stats64 *s) | |
c2ec5f2e | 2406 | { |
6ca80638 | 2407 | struct dsa_port *dp = dsa_user_to_port(dev); |
c2ec5f2e OR |
2408 | struct dsa_switch *ds = dp->ds; |
2409 | ||
2410 | if (ds->ops->get_stats64) | |
2411 | ds->ops->get_stats64(ds, dp->index, s); | |
2412 | else | |
2413 | dev_get_tstats64(dev, s); | |
2414 | } | |
2415 | ||
6ca80638 FF |
2416 | static int dsa_user_fill_forward_path(struct net_device_path_ctx *ctx, |
2417 | struct net_device_path *path) | |
0994d492 | 2418 | { |
6ca80638 FF |
2419 | struct dsa_port *dp = dsa_user_to_port(ctx->dev); |
2420 | struct net_device *conduit = dsa_port_to_conduit(dp); | |
0994d492 FF |
2421 | struct dsa_port *cpu_dp = dp->cpu_dp; |
2422 | ||
2423 | path->dev = ctx->dev; | |
2424 | path->type = DEV_PATH_DSA; | |
2425 | path->dsa.proto = cpu_dp->tag_ops->proto; | |
2426 | path->dsa.port = dp->index; | |
6ca80638 | 2427 | ctx->dev = conduit; |
0994d492 FF |
2428 | |
2429 | return 0; | |
2430 | } | |
2431 | ||
6ca80638 FF |
2432 | static const struct net_device_ops dsa_user_netdev_ops = { |
2433 | .ndo_open = dsa_user_open, | |
2434 | .ndo_stop = dsa_user_close, | |
2435 | .ndo_start_xmit = dsa_user_xmit, | |
2436 | .ndo_change_rx_flags = dsa_user_change_rx_flags, | |
2437 | .ndo_set_rx_mode = dsa_user_set_rx_mode, | |
2438 | .ndo_set_mac_address = dsa_user_set_mac_address, | |
2439 | .ndo_fdb_dump = dsa_user_fdb_dump, | |
2440 | .ndo_eth_ioctl = dsa_user_ioctl, | |
2441 | .ndo_get_iflink = dsa_user_get_iflink, | |
04ff53f9 | 2442 | #ifdef CONFIG_NET_POLL_CONTROLLER |
6ca80638 FF |
2443 | .ndo_netpoll_setup = dsa_user_netpoll_setup, |
2444 | .ndo_netpoll_cleanup = dsa_user_netpoll_cleanup, | |
2445 | .ndo_poll_controller = dsa_user_poll_controller, | |
04ff53f9 | 2446 | #endif |
6ca80638 FF |
2447 | .ndo_setup_tc = dsa_user_setup_tc, |
2448 | .ndo_get_stats64 = dsa_user_get_stats64, | |
2449 | .ndo_vlan_rx_add_vid = dsa_user_vlan_rx_add_vid, | |
2450 | .ndo_vlan_rx_kill_vid = dsa_user_vlan_rx_kill_vid, | |
2451 | .ndo_change_mtu = dsa_user_change_mtu, | |
2452 | .ndo_fill_forward_path = dsa_user_fill_forward_path, | |
98237d43 SF |
2453 | }; |
2454 | ||
f37db85d FF |
2455 | static struct device_type dsa_type = { |
2456 | .name = "dsa", | |
2457 | }; | |
2458 | ||
aab9c406 FF |
2459 | void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up) |
2460 | { | |
2461 | const struct dsa_port *dp = dsa_to_port(ds, port); | |
2462 | ||
765bda93 RK |
2463 | if (dp->pl) |
2464 | phylink_mac_change(dp->pl, up); | |
aab9c406 FF |
2465 | } |
2466 | EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change); | |
2467 | ||
6ca80638 FF |
2468 | static void dsa_user_phylink_fixed_state(struct phylink_config *config, |
2469 | struct phylink_link_state *state) | |
aab9c406 | 2470 | { |
5c05c1db | 2471 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); |
aab9c406 FF |
2472 | struct dsa_switch *ds = dp->ds; |
2473 | ||
2474 | /* No need to check that this operation is valid, the callback would | |
2475 | * not be called if it was not. | |
2476 | */ | |
2477 | ds->ops->phylink_fixed_state(ds, dp->index, state); | |
ce31b31c FF |
2478 | } |
2479 | ||
6ca80638 FF |
2480 | /* user device setup *******************************************************/ |
2481 | static int dsa_user_phy_connect(struct net_device *user_dev, int addr, | |
2482 | u32 flags) | |
c305c165 | 2483 | { |
6ca80638 | 2484 | struct dsa_port *dp = dsa_user_to_port(user_dev); |
d945097b | 2485 | struct dsa_switch *ds = dp->ds; |
c305c165 | 2486 | |
6ca80638 FF |
2487 | user_dev->phydev = mdiobus_get_phy(ds->user_mii_bus, addr); |
2488 | if (!user_dev->phydev) { | |
2489 | netdev_err(user_dev, "no phy at %d\n", addr); | |
c305c165 | 2490 | return -ENODEV; |
d25b8e74 | 2491 | } |
c305c165 | 2492 | |
6ca80638 | 2493 | user_dev->phydev->dev_flags |= flags; |
c916e8e1 | 2494 | |
6ca80638 | 2495 | return phylink_connect_phy(dp->pl, user_dev->phydev); |
11d8f3dd | 2496 | } |
11d8f3dd | 2497 | |
6ca80638 | 2498 | static int dsa_user_phy_setup(struct net_device *user_dev) |
0d8bcdd3 | 2499 | { |
6ca80638 | 2500 | struct dsa_port *dp = dsa_user_to_port(user_dev); |
d945097b VD |
2501 | struct device_node *port_dn = dp->dn; |
2502 | struct dsa_switch *ds = dp->ds; | |
6819563e | 2503 | u32 phy_flags = 0; |
0c65b2b9 | 2504 | int ret; |
0d8bcdd3 | 2505 | |
6ca80638 | 2506 | dp->pl_config.dev = &user_dev->dev; |
44cc27e4 IC |
2507 | dp->pl_config.type = PHYLINK_NETDEV; |
2508 | ||
5c05c1db RK |
2509 | /* The get_fixed_state callback takes precedence over polling the |
2510 | * link GPIO in PHYLINK (see phylink_get_fixed_state). Only set | |
2511 | * this if the switch provides such a callback. | |
2512 | */ | |
2513 | if (ds->ops->phylink_fixed_state) { | |
6ca80638 | 2514 | dp->pl_config.get_fixed_state = dsa_user_phylink_fixed_state; |
5c05c1db RK |
2515 | dp->pl_config.poll_fixed_state = true; |
2516 | } | |
2517 | ||
21bd64bd RKO |
2518 | ret = dsa_port_phylink_create(dp); |
2519 | if (ret) | |
2520 | return ret; | |
0d8bcdd3 | 2521 | |
9d490b4e | 2522 | if (ds->ops->get_phy_flags) |
d945097b | 2523 | phy_flags = ds->ops->get_phy_flags(ds, dp->index); |
6819563e | 2524 | |
aab9c406 | 2525 | ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags); |
6ca80638 | 2526 | if (ret == -ENODEV && ds->user_mii_bus) { |
6146dd45 VO |
2527 | /* We could not connect to a designated PHY or SFP, so try to |
2528 | * use the switch internal MDIO bus instead | |
aab9c406 | 2529 | */ |
6ca80638 | 2530 | ret = dsa_user_phy_connect(user_dev, dp->index, phy_flags); |
6a52e733 VO |
2531 | } |
2532 | if (ret) { | |
6ca80638 | 2533 | netdev_err(user_dev, "failed to connect to PHY: %pe\n", |
6a52e733 | 2534 | ERR_PTR(ret)); |
cf5ca4dd | 2535 | dsa_port_phylink_destroy(dp); |
b31f65fb | 2536 | } |
9697f1cd | 2537 | |
6146dd45 | 2538 | return ret; |
0d8bcdd3 FF |
2539 | } |
2540 | ||
6ca80638 | 2541 | void dsa_user_setup_tagger(struct net_device *user) |
53da0eba | 2542 | { |
6ca80638 FF |
2543 | struct dsa_port *dp = dsa_user_to_port(user); |
2544 | struct net_device *conduit = dsa_port_to_conduit(dp); | |
2545 | struct dsa_user_priv *p = netdev_priv(user); | |
53da0eba | 2546 | const struct dsa_port *cpu_dp = dp->cpu_dp; |
58adf9dc | 2547 | const struct dsa_switch *ds = dp->ds; |
53da0eba | 2548 | |
6ca80638 FF |
2549 | user->needed_headroom = cpu_dp->tag_ops->needed_headroom; |
2550 | user->needed_tailroom = cpu_dp->tag_ops->needed_tailroom; | |
2551 | /* Try to save one extra realloc later in the TX path (in the conduit) | |
2552 | * by also inheriting the conduit's needed headroom and tailroom. | |
53da0eba VO |
2553 | * The 8021q driver also does this. |
2554 | */ | |
6ca80638 FF |
2555 | user->needed_headroom += conduit->needed_headroom; |
2556 | user->needed_tailroom += conduit->needed_tailroom; | |
53da0eba VO |
2557 | |
2558 | p->xmit = cpu_dp->tag_ops->xmit; | |
21cf377a | 2559 | |
6ca80638 FF |
2560 | user->features = conduit->vlan_features | NETIF_F_HW_TC; |
2561 | user->hw_features |= NETIF_F_HW_TC; | |
2562 | user->features |= NETIF_F_LLTX; | |
2563 | if (user->needed_tailroom) | |
2564 | user->features &= ~(NETIF_F_SG | NETIF_F_FRAGLIST); | |
58adf9dc | 2565 | if (ds->needs_standalone_vlan_filtering) |
6ca80638 | 2566 | user->features |= NETIF_F_HW_VLAN_CTAG_FILTER; |
53da0eba VO |
2567 | } |
2568 | ||
6ca80638 | 2569 | int dsa_user_suspend(struct net_device *user_dev) |
24462549 | 2570 | { |
6ca80638 | 2571 | struct dsa_port *dp = dsa_user_to_port(user_dev); |
24462549 | 2572 | |
6ca80638 | 2573 | if (!netif_running(user_dev)) |
a94c689e FF |
2574 | return 0; |
2575 | ||
6ca80638 | 2576 | netif_device_detach(user_dev); |
f154be24 | 2577 | |
aab9c406 FF |
2578 | rtnl_lock(); |
2579 | phylink_stop(dp->pl); | |
2580 | rtnl_unlock(); | |
24462549 FF |
2581 | |
2582 | return 0; | |
2583 | } | |
2584 | ||
6ca80638 | 2585 | int dsa_user_resume(struct net_device *user_dev) |
24462549 | 2586 | { |
6ca80638 | 2587 | struct dsa_port *dp = dsa_user_to_port(user_dev); |
aab9c406 | 2588 | |
6ca80638 | 2589 | if (!netif_running(user_dev)) |
a94c689e FF |
2590 | return 0; |
2591 | ||
6ca80638 | 2592 | netif_device_attach(user_dev); |
24462549 | 2593 | |
aab9c406 FF |
2594 | rtnl_lock(); |
2595 | phylink_start(dp->pl); | |
2596 | rtnl_unlock(); | |
24462549 FF |
2597 | |
2598 | return 0; | |
2599 | } | |
2600 | ||
6ca80638 | 2601 | int dsa_user_create(struct dsa_port *port) |
91da11f8 | 2602 | { |
6ca80638 | 2603 | struct net_device *conduit = dsa_port_to_conduit(port); |
4cfbf09c | 2604 | struct dsa_switch *ds = port->ds; |
6ca80638 FF |
2605 | struct net_device *user_dev; |
2606 | struct dsa_user_priv *p; | |
0171a1d2 RV |
2607 | const char *name; |
2608 | int assign_type; | |
91da11f8 LB |
2609 | int ret; |
2610 | ||
55199df6 FF |
2611 | if (!ds->num_tx_queues) |
2612 | ds->num_tx_queues = 1; | |
2613 | ||
0171a1d2 RV |
2614 | if (port->name) { |
2615 | name = port->name; | |
6fdb0384 | 2616 | assign_type = NET_NAME_PREDICTABLE; |
0171a1d2 RV |
2617 | } else { |
2618 | name = "eth%d"; | |
b8790661 | 2619 | assign_type = NET_NAME_ENUM; |
0171a1d2 RV |
2620 | } |
2621 | ||
6ca80638 FF |
2622 | user_dev = alloc_netdev_mqs(sizeof(struct dsa_user_priv), name, |
2623 | assign_type, ether_setup, | |
2624 | ds->num_tx_queues, 1); | |
2625 | if (user_dev == NULL) | |
d87d6f44 | 2626 | return -ENOMEM; |
91da11f8 | 2627 | |
6ca80638 FF |
2628 | user_dev->rtnl_link_ops = &dsa_link_ops; |
2629 | user_dev->ethtool_ops = &dsa_user_ethtool_ops; | |
d538eca8 | 2630 | #if IS_ENABLED(CONFIG_DCB) |
6ca80638 | 2631 | user_dev->dcbnl_ops = &dsa_user_dcbnl_ops; |
d538eca8 | 2632 | #endif |
83216e39 | 2633 | if (!is_zero_ether_addr(port->mac)) |
6ca80638 | 2634 | eth_hw_addr_set(user_dev, port->mac); |
a2c7023f | 2635 | else |
6ca80638 FF |
2636 | eth_hw_addr_inherit(user_dev, conduit); |
2637 | user_dev->priv_flags |= IFF_NO_QUEUE; | |
5e8a1e03 | 2638 | if (dsa_switch_supports_uc_filtering(ds)) |
6ca80638 FF |
2639 | user_dev->priv_flags |= IFF_UNICAST_FLT; |
2640 | user_dev->netdev_ops = &dsa_user_netdev_ops; | |
bfcb8132 | 2641 | if (ds->ops->port_max_mtu) |
6ca80638 FF |
2642 | user_dev->max_mtu = ds->ops->port_max_mtu(ds, port->index); |
2643 | SET_NETDEV_DEVTYPE(user_dev, &dsa_type); | |
2644 | ||
2645 | SET_NETDEV_DEV(user_dev, port->ds->dev); | |
2646 | SET_NETDEV_DEVLINK_PORT(user_dev, &port->devlink_port); | |
2647 | user_dev->dev.of_node = port->dn; | |
2648 | user_dev->vlan_features = conduit->vlan_features; | |
2649 | ||
2650 | p = netdev_priv(user_dev); | |
2651 | user_dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); | |
2652 | if (!user_dev->tstats) { | |
2653 | free_netdev(user_dev); | |
5f6b4e14 FF |
2654 | return -ENOMEM; |
2655 | } | |
e131a563 | 2656 | |
6ca80638 | 2657 | ret = gro_cells_init(&p->gcells, user_dev); |
e131a563 AL |
2658 | if (ret) |
2659 | goto out_free; | |
2660 | ||
4cfbf09c | 2661 | p->dp = port; |
f50f2127 | 2662 | INIT_LIST_HEAD(&p->mall_tc_list); |
6ca80638 FF |
2663 | port->user = user_dev; |
2664 | dsa_user_setup_tagger(user_dev); | |
91da11f8 | 2665 | |
6ca80638 | 2666 | netif_carrier_off(user_dev); |
91da11f8 | 2667 | |
6ca80638 | 2668 | ret = dsa_user_phy_setup(user_dev); |
0071f56e | 2669 | if (ret) { |
6ca80638 | 2670 | netdev_err(user_dev, |
c9ebf126 VO |
2671 | "error %d setting up PHY for tree %d, switch %d, port %d\n", |
2672 | ret, ds->dst->index, ds->index, port->index); | |
e131a563 | 2673 | goto out_gcells; |
e804441c FF |
2674 | } |
2675 | ||
904e112a | 2676 | rtnl_lock(); |
e31dbd3b | 2677 | |
6ca80638 | 2678 | ret = dsa_user_change_mtu(user_dev, ETH_DATA_LEN); |
904e112a VO |
2679 | if (ret && ret != -EOPNOTSUPP) |
2680 | dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n", | |
2681 | ret, ETH_DATA_LEN, port->index); | |
2682 | ||
6ca80638 | 2683 | ret = register_netdevice(user_dev); |
e804441c | 2684 | if (ret) { |
6ca80638 FF |
2685 | netdev_err(conduit, "error %d registering interface %s\n", |
2686 | ret, user_dev->name); | |
2f1e8ea7 | 2687 | rtnl_unlock(); |
e804441c | 2688 | goto out_phy; |
0071f56e AL |
2689 | } |
2690 | ||
d538eca8 | 2691 | if (IS_ENABLED(CONFIG_DCB)) { |
6ca80638 | 2692 | ret = dsa_user_dcbnl_init(user_dev); |
d538eca8 | 2693 | if (ret) { |
6ca80638 | 2694 | netdev_err(user_dev, |
d538eca8 VO |
2695 | "failed to initialize DCB: %pe\n", |
2696 | ERR_PTR(ret)); | |
2697 | rtnl_unlock(); | |
2698 | goto out_unregister; | |
2699 | } | |
2700 | } | |
2701 | ||
6ca80638 | 2702 | ret = netdev_upper_dev_link(conduit, user_dev, NULL); |
2f1e8ea7 VO |
2703 | |
2704 | rtnl_unlock(); | |
2705 | ||
2706 | if (ret) | |
2707 | goto out_unregister; | |
2708 | ||
d87d6f44 | 2709 | return 0; |
e804441c | 2710 | |
2f1e8ea7 | 2711 | out_unregister: |
6ca80638 | 2712 | unregister_netdev(user_dev); |
e804441c | 2713 | out_phy: |
aab9c406 FF |
2714 | rtnl_lock(); |
2715 | phylink_disconnect_phy(p->dp->pl); | |
2716 | rtnl_unlock(); | |
cf5ca4dd | 2717 | dsa_port_phylink_destroy(p->dp); |
e131a563 AL |
2718 | out_gcells: |
2719 | gro_cells_destroy(&p->gcells); | |
e804441c | 2720 | out_free: |
6ca80638 FF |
2721 | free_percpu(user_dev->tstats); |
2722 | free_netdev(user_dev); | |
2723 | port->user = NULL; | |
e804441c | 2724 | return ret; |
91da11f8 | 2725 | } |
b73adef6 | 2726 | |
6ca80638 | 2727 | void dsa_user_destroy(struct net_device *user_dev) |
cda5c15b | 2728 | { |
6ca80638 FF |
2729 | struct net_device *conduit = dsa_user_to_conduit(user_dev); |
2730 | struct dsa_port *dp = dsa_user_to_port(user_dev); | |
2731 | struct dsa_user_priv *p = netdev_priv(user_dev); | |
cda5c15b | 2732 | |
6ca80638 | 2733 | netif_carrier_off(user_dev); |
aab9c406 | 2734 | rtnl_lock(); |
6ca80638 FF |
2735 | netdev_upper_dev_unlink(conduit, user_dev); |
2736 | unregister_netdevice(user_dev); | |
aab9c406 FF |
2737 | phylink_disconnect_phy(dp->pl); |
2738 | rtnl_unlock(); | |
881eadab | 2739 | |
cf5ca4dd | 2740 | dsa_port_phylink_destroy(dp); |
e131a563 | 2741 | gro_cells_destroy(&p->gcells); |
6ca80638 FF |
2742 | free_percpu(user_dev->tstats); |
2743 | free_netdev(user_dev); | |
cda5c15b NA |
2744 | } |
2745 | ||
6ca80638 | 2746 | int dsa_user_change_conduit(struct net_device *dev, struct net_device *conduit, |
95f510d0 VO |
2747 | struct netlink_ext_ack *extack) |
2748 | { | |
6ca80638 FF |
2749 | struct net_device *old_conduit = dsa_user_to_conduit(dev); |
2750 | struct dsa_port *dp = dsa_user_to_port(dev); | |
95f510d0 VO |
2751 | struct dsa_switch *ds = dp->ds; |
2752 | struct net_device *upper; | |
2753 | struct list_head *iter; | |
2754 | int err; | |
2755 | ||
6ca80638 | 2756 | if (conduit == old_conduit) |
95f510d0 VO |
2757 | return 0; |
2758 | ||
6ca80638 | 2759 | if (!ds->ops->port_change_conduit) { |
95f510d0 | 2760 | NL_SET_ERR_MSG_MOD(extack, |
6ca80638 | 2761 | "Driver does not support changing DSA conduit"); |
95f510d0 VO |
2762 | return -EOPNOTSUPP; |
2763 | } | |
2764 | ||
6ca80638 | 2765 | if (!netdev_uses_dsa(conduit)) { |
95f510d0 | 2766 | NL_SET_ERR_MSG_MOD(extack, |
6ca80638 | 2767 | "Interface not eligible as DSA conduit"); |
95f510d0 VO |
2768 | return -EOPNOTSUPP; |
2769 | } | |
2770 | ||
6ca80638 FF |
2771 | netdev_for_each_upper_dev_rcu(conduit, upper, iter) { |
2772 | if (dsa_user_dev_check(upper)) | |
95f510d0 VO |
2773 | continue; |
2774 | if (netif_is_bridge_master(upper)) | |
2775 | continue; | |
6ca80638 | 2776 | NL_SET_ERR_MSG_MOD(extack, "Cannot join conduit with unknown uppers"); |
95f510d0 VO |
2777 | return -EOPNOTSUPP; |
2778 | } | |
2779 | ||
6ca80638 FF |
2780 | /* Since we allow live-changing the DSA conduit, plus we auto-open the |
2781 | * DSA conduit when the user port opens => we need to ensure that the | |
2782 | * new DSA conduit is open too. | |
95f510d0 VO |
2783 | */ |
2784 | if (dev->flags & IFF_UP) { | |
6ca80638 | 2785 | err = dev_open(conduit, extack); |
95f510d0 VO |
2786 | if (err) |
2787 | return err; | |
2788 | } | |
2789 | ||
6ca80638 | 2790 | netdev_upper_dev_unlink(old_conduit, dev); |
95f510d0 | 2791 | |
6ca80638 | 2792 | err = netdev_upper_dev_link(conduit, dev, extack); |
95f510d0 | 2793 | if (err) |
6ca80638 | 2794 | goto out_revert_old_conduit_unlink; |
95f510d0 | 2795 | |
6ca80638 | 2796 | err = dsa_port_change_conduit(dp, conduit, extack); |
95f510d0 | 2797 | if (err) |
6ca80638 | 2798 | goto out_revert_conduit_link; |
95f510d0 VO |
2799 | |
2800 | /* Update the MTU of the new CPU port through cross-chip notifiers */ | |
6ca80638 | 2801 | err = dsa_user_change_mtu(dev, dev->mtu); |
95f510d0 VO |
2802 | if (err && err != -EOPNOTSUPP) { |
2803 | netdev_warn(dev, | |
6ca80638 | 2804 | "nonfatal error updating MTU with new conduit: %pe\n", |
95f510d0 VO |
2805 | ERR_PTR(err)); |
2806 | } | |
2807 | ||
2808 | /* If the port doesn't have its own MAC address and relies on the DSA | |
6ca80638 | 2809 | * conduit's one, inherit it again from the new DSA conduit. |
95f510d0 VO |
2810 | */ |
2811 | if (is_zero_ether_addr(dp->mac)) | |
6ca80638 | 2812 | eth_hw_addr_inherit(dev, conduit); |
95f510d0 VO |
2813 | |
2814 | return 0; | |
2815 | ||
6ca80638 FF |
2816 | out_revert_conduit_link: |
2817 | netdev_upper_dev_unlink(conduit, dev); | |
2818 | out_revert_old_conduit_unlink: | |
2819 | netdev_upper_dev_link(old_conduit, dev, NULL); | |
95f510d0 VO |
2820 | return err; |
2821 | } | |
2822 | ||
6ca80638 | 2823 | bool dsa_user_dev_check(const struct net_device *dev) |
b73adef6 | 2824 | { |
6ca80638 | 2825 | return dev->netdev_ops == &dsa_user_netdev_ops; |
b73adef6 | 2826 | } |
6ca80638 | 2827 | EXPORT_SYMBOL_GPL(dsa_user_dev_check); |
b73adef6 | 2828 | |
6ca80638 FF |
2829 | static int dsa_user_changeupper(struct net_device *dev, |
2830 | struct netdev_notifier_changeupper_info *info) | |
b73adef6 | 2831 | { |
6ca80638 | 2832 | struct dsa_port *dp = dsa_user_to_port(dev); |
2afc526a | 2833 | struct netlink_ext_ack *extack; |
8e92ab3a | 2834 | int err = NOTIFY_DONE; |
b73adef6 | 2835 | |
6ca80638 | 2836 | if (!dsa_user_dev_check(dev)) |
4c3f80d2 VO |
2837 | return err; |
2838 | ||
2afc526a VO |
2839 | extack = netdev_notifier_info_to_extack(&info->info); |
2840 | ||
8e92ab3a VD |
2841 | if (netif_is_bridge_master(info->upper_dev)) { |
2842 | if (info->linking) { | |
2afc526a | 2843 | err = dsa_port_bridge_join(dp, info->upper_dev, extack); |
bff33f7e VO |
2844 | if (!err) |
2845 | dsa_bridge_mtu_normalization(dp); | |
67b5fb5d | 2846 | if (err == -EOPNOTSUPP) { |
d795527d VO |
2847 | NL_SET_ERR_MSG_WEAK_MOD(extack, |
2848 | "Offloading not supported"); | |
67b5fb5d VO |
2849 | err = 0; |
2850 | } | |
8e92ab3a VD |
2851 | err = notifier_from_errno(err); |
2852 | } else { | |
17d7802b | 2853 | dsa_port_bridge_leave(dp, info->upper_dev); |
8e92ab3a | 2854 | err = NOTIFY_OK; |
6debb68a | 2855 | } |
058102a6 TW |
2856 | } else if (netif_is_lag_master(info->upper_dev)) { |
2857 | if (info->linking) { | |
2858 | err = dsa_port_lag_join(dp, info->upper_dev, | |
2afc526a | 2859 | info->upper_info, extack); |
058102a6 | 2860 | if (err == -EOPNOTSUPP) { |
d795527d VO |
2861 | NL_SET_ERR_MSG_WEAK_MOD(extack, |
2862 | "Offloading not supported"); | |
058102a6 TW |
2863 | err = 0; |
2864 | } | |
2865 | err = notifier_from_errno(err); | |
2866 | } else { | |
2867 | dsa_port_lag_leave(dp, info->upper_dev); | |
2868 | err = NOTIFY_OK; | |
2869 | } | |
18596f50 GM |
2870 | } else if (is_hsr_master(info->upper_dev)) { |
2871 | if (info->linking) { | |
fefe5dc4 | 2872 | err = dsa_port_hsr_join(dp, info->upper_dev, extack); |
18596f50 | 2873 | if (err == -EOPNOTSUPP) { |
d795527d VO |
2874 | NL_SET_ERR_MSG_WEAK_MOD(extack, |
2875 | "Offloading not supported"); | |
18596f50 GM |
2876 | err = 0; |
2877 | } | |
2878 | err = notifier_from_errno(err); | |
2879 | } else { | |
2880 | dsa_port_hsr_leave(dp, info->upper_dev); | |
2881 | err = NOTIFY_OK; | |
2882 | } | |
058102a6 TW |
2883 | } |
2884 | ||
2885 | return err; | |
2886 | } | |
2887 | ||
6ca80638 FF |
2888 | static int dsa_user_prechangeupper(struct net_device *dev, |
2889 | struct netdev_notifier_changeupper_info *info) | |
74918945 | 2890 | { |
6ca80638 | 2891 | struct dsa_port *dp = dsa_user_to_port(dev); |
74918945 | 2892 | |
6ca80638 | 2893 | if (!dsa_user_dev_check(dev)) |
4c3f80d2 VO |
2894 | return NOTIFY_DONE; |
2895 | ||
74918945 | 2896 | if (netif_is_bridge_master(info->upper_dev) && !info->linking) |
4e51bf44 | 2897 | dsa_port_pre_bridge_leave(dp, info->upper_dev); |
74918945 | 2898 | else if (netif_is_lag_master(info->upper_dev) && !info->linking) |
4e51bf44 | 2899 | dsa_port_pre_lag_leave(dp, info->upper_dev); |
6ca80638 FF |
2900 | /* dsa_port_pre_hsr_leave is not yet necessary since hsr devices cannot |
2901 | * meaningfully placed under a bridge yet | |
74918945 VO |
2902 | */ |
2903 | ||
4e51bf44 | 2904 | return NOTIFY_DONE; |
74918945 VO |
2905 | } |
2906 | ||
058102a6 | 2907 | static int |
6ca80638 FF |
2908 | dsa_user_lag_changeupper(struct net_device *dev, |
2909 | struct netdev_notifier_changeupper_info *info) | |
058102a6 TW |
2910 | { |
2911 | struct net_device *lower; | |
2912 | struct list_head *iter; | |
2913 | int err = NOTIFY_DONE; | |
2914 | struct dsa_port *dp; | |
2915 | ||
4c3f80d2 VO |
2916 | if (!netif_is_lag_master(dev)) |
2917 | return err; | |
2918 | ||
058102a6 | 2919 | netdev_for_each_lower_dev(dev, lower, iter) { |
6ca80638 | 2920 | if (!dsa_user_dev_check(lower)) |
058102a6 TW |
2921 | continue; |
2922 | ||
6ca80638 | 2923 | dp = dsa_user_to_port(lower); |
dedd6a00 | 2924 | if (!dp->lag) |
058102a6 TW |
2925 | /* Software LAG */ |
2926 | continue; | |
2927 | ||
6ca80638 | 2928 | err = dsa_user_changeupper(lower, info); |
058102a6 TW |
2929 | if (notifier_to_errno(err)) |
2930 | break; | |
6debb68a | 2931 | } |
b73adef6 | 2932 | |
8e92ab3a | 2933 | return err; |
6debb68a | 2934 | } |
b73adef6 | 2935 | |
6ca80638 FF |
2936 | /* Same as dsa_user_lag_changeupper() except that it calls |
2937 | * dsa_user_prechangeupper() | |
74918945 VO |
2938 | */ |
2939 | static int | |
6ca80638 FF |
2940 | dsa_user_lag_prechangeupper(struct net_device *dev, |
2941 | struct netdev_notifier_changeupper_info *info) | |
74918945 VO |
2942 | { |
2943 | struct net_device *lower; | |
2944 | struct list_head *iter; | |
2945 | int err = NOTIFY_DONE; | |
2946 | struct dsa_port *dp; | |
2947 | ||
4c3f80d2 VO |
2948 | if (!netif_is_lag_master(dev)) |
2949 | return err; | |
2950 | ||
74918945 | 2951 | netdev_for_each_lower_dev(dev, lower, iter) { |
6ca80638 | 2952 | if (!dsa_user_dev_check(lower)) |
74918945 VO |
2953 | continue; |
2954 | ||
6ca80638 | 2955 | dp = dsa_user_to_port(lower); |
dedd6a00 | 2956 | if (!dp->lag) |
74918945 VO |
2957 | /* Software LAG */ |
2958 | continue; | |
2959 | ||
6ca80638 | 2960 | err = dsa_user_prechangeupper(lower, info); |
74918945 VO |
2961 | if (notifier_to_errno(err)) |
2962 | break; | |
2963 | } | |
2964 | ||
2965 | return err; | |
2966 | } | |
2967 | ||
eb46e8da VO |
2968 | static int |
2969 | dsa_prevent_bridging_8021q_upper(struct net_device *dev, | |
2970 | struct netdev_notifier_changeupper_info *info) | |
cc1d5bda FF |
2971 | { |
2972 | struct netlink_ext_ack *ext_ack; | |
6ca80638 | 2973 | struct net_device *user, *br; |
cc1d5bda FF |
2974 | struct dsa_port *dp; |
2975 | ||
2976 | ext_ack = netdev_notifier_info_to_extack(&info->info); | |
2977 | ||
2978 | if (!is_vlan_dev(dev)) | |
2979 | return NOTIFY_DONE; | |
2980 | ||
6ca80638 FF |
2981 | user = vlan_dev_real_dev(dev); |
2982 | if (!dsa_user_dev_check(user)) | |
cc1d5bda FF |
2983 | return NOTIFY_DONE; |
2984 | ||
6ca80638 | 2985 | dp = dsa_user_to_port(user); |
36cbf39b VO |
2986 | br = dsa_port_bridge_dev_get(dp); |
2987 | if (!br) | |
cc1d5bda FF |
2988 | return NOTIFY_DONE; |
2989 | ||
2990 | /* Deny enslaving a VLAN device into a VLAN-aware bridge */ | |
36cbf39b | 2991 | if (br_vlan_enabled(br) && |
cc1d5bda FF |
2992 | netif_is_bridge_master(info->upper_dev) && info->linking) { |
2993 | NL_SET_ERR_MSG_MOD(ext_ack, | |
6ca80638 | 2994 | "Cannot make VLAN device join VLAN-aware bridge"); |
cc1d5bda FF |
2995 | return notifier_from_errno(-EINVAL); |
2996 | } | |
2997 | ||
2998 | return NOTIFY_DONE; | |
2999 | } | |
3000 | ||
2b138406 | 3001 | static int |
6ca80638 FF |
3002 | dsa_user_check_8021q_upper(struct net_device *dev, |
3003 | struct netdev_notifier_changeupper_info *info) | |
2b138406 | 3004 | { |
6ca80638 | 3005 | struct dsa_port *dp = dsa_user_to_port(dev); |
36cbf39b | 3006 | struct net_device *br = dsa_port_bridge_dev_get(dp); |
2b138406 VO |
3007 | struct bridge_vlan_info br_info; |
3008 | struct netlink_ext_ack *extack; | |
3009 | int err = NOTIFY_DONE; | |
3010 | u16 vid; | |
3011 | ||
adb256eb | 3012 | if (!br || !br_vlan_enabled(br)) |
2b138406 VO |
3013 | return NOTIFY_DONE; |
3014 | ||
3015 | extack = netdev_notifier_info_to_extack(&info->info); | |
3016 | vid = vlan_dev_vlan_id(info->upper_dev); | |
3017 | ||
3018 | /* br_vlan_get_info() returns -EINVAL or -ENOENT if the | |
3019 | * device, respectively the VID is not found, returning | |
3020 | * 0 means success, which is a failure for us here. | |
3021 | */ | |
3022 | err = br_vlan_get_info(br, vid, &br_info); | |
3023 | if (err == 0) { | |
3024 | NL_SET_ERR_MSG_MOD(extack, | |
3025 | "This VLAN is already configured by the bridge"); | |
3026 | return notifier_from_errno(-EBUSY); | |
3027 | } | |
3028 | ||
3029 | return NOTIFY_DONE; | |
3030 | } | |
3031 | ||
4ede74e7 | 3032 | static int |
6ca80638 FF |
3033 | dsa_user_prechangeupper_sanity_check(struct net_device *dev, |
3034 | struct netdev_notifier_changeupper_info *info) | |
4ede74e7 VO |
3035 | { |
3036 | struct dsa_switch *ds; | |
3037 | struct dsa_port *dp; | |
3038 | int err; | |
3039 | ||
6ca80638 | 3040 | if (!dsa_user_dev_check(dev)) |
4ede74e7 VO |
3041 | return dsa_prevent_bridging_8021q_upper(dev, info); |
3042 | ||
6ca80638 | 3043 | dp = dsa_user_to_port(dev); |
4ede74e7 VO |
3044 | ds = dp->ds; |
3045 | ||
3046 | if (ds->ops->port_prechangeupper) { | |
3047 | err = ds->ops->port_prechangeupper(ds, dp->index, info); | |
3048 | if (err) | |
3049 | return notifier_from_errno(err); | |
3050 | } | |
3051 | ||
3052 | if (is_vlan_dev(info->upper_dev)) | |
6ca80638 | 3053 | return dsa_user_check_8021q_upper(dev, info); |
4ede74e7 VO |
3054 | |
3055 | return NOTIFY_DONE; | |
3056 | } | |
3057 | ||
6ca80638 FF |
3058 | /* To be eligible as a DSA conduit, a LAG must have all lower interfaces be |
3059 | * eligible DSA conduits. Additionally, all LAG slaves must be DSA conduits of | |
acc43b7b VO |
3060 | * switches in the same switch tree. |
3061 | */ | |
6ca80638 FF |
3062 | static int dsa_lag_conduit_validate(struct net_device *lag_dev, |
3063 | struct netlink_ext_ack *extack) | |
acc43b7b VO |
3064 | { |
3065 | struct net_device *lower1, *lower2; | |
3066 | struct list_head *iter1, *iter2; | |
3067 | ||
3068 | netdev_for_each_lower_dev(lag_dev, lower1, iter1) { | |
3069 | netdev_for_each_lower_dev(lag_dev, lower2, iter2) { | |
3070 | if (!netdev_uses_dsa(lower1) || | |
3071 | !netdev_uses_dsa(lower2)) { | |
3072 | NL_SET_ERR_MSG_MOD(extack, | |
6ca80638 | 3073 | "All LAG ports must be eligible as DSA conduits"); |
acc43b7b VO |
3074 | return notifier_from_errno(-EINVAL); |
3075 | } | |
3076 | ||
3077 | if (lower1 == lower2) | |
3078 | continue; | |
3079 | ||
3080 | if (!dsa_port_tree_same(lower1->dsa_ptr, | |
3081 | lower2->dsa_ptr)) { | |
3082 | NL_SET_ERR_MSG_MOD(extack, | |
6ca80638 | 3083 | "LAG contains DSA conduits of disjoint switch trees"); |
acc43b7b VO |
3084 | return notifier_from_errno(-EINVAL); |
3085 | } | |
3086 | } | |
3087 | } | |
3088 | ||
3089 | return NOTIFY_DONE; | |
3090 | } | |
3091 | ||
4f03dcc6 | 3092 | static int |
6ca80638 FF |
3093 | dsa_conduit_prechangeupper_sanity_check(struct net_device *conduit, |
3094 | struct netdev_notifier_changeupper_info *info) | |
4f03dcc6 | 3095 | { |
acc43b7b | 3096 | struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info); |
4f03dcc6 | 3097 | |
6ca80638 | 3098 | if (!netdev_uses_dsa(conduit)) |
4f03dcc6 VO |
3099 | return NOTIFY_DONE; |
3100 | ||
3101 | if (!info->linking) | |
3102 | return NOTIFY_DONE; | |
3103 | ||
3104 | /* Allow DSA switch uppers */ | |
6ca80638 | 3105 | if (dsa_user_dev_check(info->upper_dev)) |
4f03dcc6 VO |
3106 | return NOTIFY_DONE; |
3107 | ||
6ca80638 | 3108 | /* Allow bridge uppers of DSA conduits, subject to further |
4f03dcc6 VO |
3109 | * restrictions in dsa_bridge_prechangelower_sanity_check() |
3110 | */ | |
3111 | if (netif_is_bridge_master(info->upper_dev)) | |
3112 | return NOTIFY_DONE; | |
3113 | ||
acc43b7b | 3114 | /* Allow LAG uppers, subject to further restrictions in |
6ca80638 | 3115 | * dsa_lag_conduit_prechangelower_sanity_check() |
acc43b7b VO |
3116 | */ |
3117 | if (netif_is_lag_master(info->upper_dev)) | |
6ca80638 | 3118 | return dsa_lag_conduit_validate(info->upper_dev, extack); |
4f03dcc6 VO |
3119 | |
3120 | NL_SET_ERR_MSG_MOD(extack, | |
6ca80638 | 3121 | "DSA conduit cannot join unknown upper interfaces"); |
4f03dcc6 VO |
3122 | return notifier_from_errno(-EBUSY); |
3123 | } | |
3124 | ||
acc43b7b | 3125 | static int |
6ca80638 FF |
3126 | dsa_lag_conduit_prechangelower_sanity_check(struct net_device *dev, |
3127 | struct netdev_notifier_changeupper_info *info) | |
acc43b7b VO |
3128 | { |
3129 | struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(&info->info); | |
3130 | struct net_device *lag_dev = info->upper_dev; | |
3131 | struct net_device *lower; | |
3132 | struct list_head *iter; | |
3133 | ||
3134 | if (!netdev_uses_dsa(lag_dev) || !netif_is_lag_master(lag_dev)) | |
3135 | return NOTIFY_DONE; | |
3136 | ||
3137 | if (!info->linking) | |
3138 | return NOTIFY_DONE; | |
3139 | ||
3140 | if (!netdev_uses_dsa(dev)) { | |
3141 | NL_SET_ERR_MSG(extack, | |
6ca80638 | 3142 | "Only DSA conduits can join a LAG DSA conduit"); |
acc43b7b VO |
3143 | return notifier_from_errno(-EINVAL); |
3144 | } | |
3145 | ||
3146 | netdev_for_each_lower_dev(lag_dev, lower, iter) { | |
3147 | if (!dsa_port_tree_same(dev->dsa_ptr, lower->dsa_ptr)) { | |
3148 | NL_SET_ERR_MSG(extack, | |
6ca80638 | 3149 | "Interface is DSA conduit for a different switch tree than this LAG"); |
acc43b7b VO |
3150 | return notifier_from_errno(-EINVAL); |
3151 | } | |
3152 | ||
3153 | break; | |
3154 | } | |
3155 | ||
3156 | return NOTIFY_DONE; | |
3157 | } | |
3158 | ||
6ca80638 | 3159 | /* Don't allow bridging of DSA conduits, since the bridge layer rx_handler |
920a33cd VO |
3160 | * prevents the DSA fake ethertype handler to be invoked, so we don't get the |
3161 | * chance to strip off and parse the DSA switch tag protocol header (the bridge | |
3162 | * layer just returns RX_HANDLER_CONSUMED, stopping RX processing for these | |
3163 | * frames). | |
3164 | * The only case where that would not be an issue is when bridging can already | |
6ca80638 | 3165 | * be offloaded, such as when the DSA conduit is itself a DSA or plain switchdev |
920a33cd VO |
3166 | * port, and is bridged only with other ports from the same hardware device. |
3167 | */ | |
3168 | static int | |
3169 | dsa_bridge_prechangelower_sanity_check(struct net_device *new_lower, | |
3170 | struct netdev_notifier_changeupper_info *info) | |
3171 | { | |
3172 | struct net_device *br = info->upper_dev; | |
3173 | struct netlink_ext_ack *extack; | |
3174 | struct net_device *lower; | |
3175 | struct list_head *iter; | |
3176 | ||
3177 | if (!netif_is_bridge_master(br)) | |
3178 | return NOTIFY_DONE; | |
3179 | ||
3180 | if (!info->linking) | |
3181 | return NOTIFY_DONE; | |
3182 | ||
3183 | extack = netdev_notifier_info_to_extack(&info->info); | |
3184 | ||
3185 | netdev_for_each_lower_dev(br, lower, iter) { | |
3186 | if (!netdev_uses_dsa(new_lower) && !netdev_uses_dsa(lower)) | |
3187 | continue; | |
3188 | ||
3189 | if (!netdev_port_same_parent_id(lower, new_lower)) { | |
3190 | NL_SET_ERR_MSG(extack, | |
6ca80638 | 3191 | "Cannot do software bridging with a DSA conduit"); |
920a33cd VO |
3192 | return notifier_from_errno(-EINVAL); |
3193 | } | |
3194 | } | |
3195 | ||
3196 | return NOTIFY_DONE; | |
3197 | } | |
3198 | ||
6ca80638 FF |
3199 | static void dsa_tree_migrate_ports_from_lag_conduit(struct dsa_switch_tree *dst, |
3200 | struct net_device *lag_dev) | |
acc43b7b | 3201 | { |
6ca80638 | 3202 | struct net_device *new_conduit = dsa_tree_find_first_conduit(dst); |
acc43b7b VO |
3203 | struct dsa_port *dp; |
3204 | int err; | |
3205 | ||
3206 | dsa_tree_for_each_user_port(dp, dst) { | |
6ca80638 | 3207 | if (dsa_port_to_conduit(dp) != lag_dev) |
acc43b7b VO |
3208 | continue; |
3209 | ||
6ca80638 | 3210 | err = dsa_user_change_conduit(dp->user, new_conduit, NULL); |
acc43b7b | 3211 | if (err) { |
6ca80638 FF |
3212 | netdev_err(dp->user, |
3213 | "failed to restore conduit to %s: %pe\n", | |
3214 | new_conduit->name, ERR_PTR(err)); | |
acc43b7b VO |
3215 | } |
3216 | } | |
3217 | } | |
3218 | ||
6ca80638 FF |
3219 | static int dsa_conduit_lag_join(struct net_device *conduit, |
3220 | struct net_device *lag_dev, | |
3221 | struct netdev_lag_upper_info *uinfo, | |
3222 | struct netlink_ext_ack *extack) | |
acc43b7b | 3223 | { |
6ca80638 | 3224 | struct dsa_port *cpu_dp = conduit->dsa_ptr; |
acc43b7b VO |
3225 | struct dsa_switch_tree *dst = cpu_dp->dst; |
3226 | struct dsa_port *dp; | |
3227 | int err; | |
3228 | ||
6ca80638 | 3229 | err = dsa_conduit_lag_setup(lag_dev, cpu_dp, uinfo, extack); |
acc43b7b VO |
3230 | if (err) |
3231 | return err; | |
3232 | ||
3233 | dsa_tree_for_each_user_port(dp, dst) { | |
6ca80638 | 3234 | if (dsa_port_to_conduit(dp) != conduit) |
acc43b7b VO |
3235 | continue; |
3236 | ||
6ca80638 | 3237 | err = dsa_user_change_conduit(dp->user, lag_dev, extack); |
acc43b7b VO |
3238 | if (err) |
3239 | goto restore; | |
3240 | } | |
3241 | ||
3242 | return 0; | |
3243 | ||
3244 | restore: | |
3245 | dsa_tree_for_each_user_port_continue_reverse(dp, dst) { | |
6ca80638 | 3246 | if (dsa_port_to_conduit(dp) != lag_dev) |
acc43b7b VO |
3247 | continue; |
3248 | ||
6ca80638 | 3249 | err = dsa_user_change_conduit(dp->user, conduit, NULL); |
acc43b7b | 3250 | if (err) { |
6ca80638 FF |
3251 | netdev_err(dp->user, |
3252 | "failed to restore conduit to %s: %pe\n", | |
3253 | conduit->name, ERR_PTR(err)); | |
acc43b7b VO |
3254 | } |
3255 | } | |
3256 | ||
6ca80638 | 3257 | dsa_conduit_lag_teardown(lag_dev, conduit->dsa_ptr); |
acc43b7b VO |
3258 | |
3259 | return err; | |
3260 | } | |
3261 | ||
6ca80638 FF |
3262 | static void dsa_conduit_lag_leave(struct net_device *conduit, |
3263 | struct net_device *lag_dev) | |
acc43b7b VO |
3264 | { |
3265 | struct dsa_port *dp, *cpu_dp = lag_dev->dsa_ptr; | |
3266 | struct dsa_switch_tree *dst = cpu_dp->dst; | |
3267 | struct dsa_port *new_cpu_dp = NULL; | |
3268 | struct net_device *lower; | |
3269 | struct list_head *iter; | |
3270 | ||
3271 | netdev_for_each_lower_dev(lag_dev, lower, iter) { | |
3272 | if (netdev_uses_dsa(lower)) { | |
3273 | new_cpu_dp = lower->dsa_ptr; | |
3274 | break; | |
3275 | } | |
3276 | } | |
3277 | ||
3278 | if (new_cpu_dp) { | |
3279 | /* Update the CPU port of the user ports still under the LAG | |
6ca80638 | 3280 | * so that dsa_port_to_conduit() continues to work properly |
acc43b7b VO |
3281 | */ |
3282 | dsa_tree_for_each_user_port(dp, dst) | |
6ca80638 | 3283 | if (dsa_port_to_conduit(dp) == lag_dev) |
acc43b7b VO |
3284 | dp->cpu_dp = new_cpu_dp; |
3285 | ||
3286 | /* Update the index of the virtual CPU port to match the lowest | |
3287 | * physical CPU port | |
3288 | */ | |
3289 | lag_dev->dsa_ptr = new_cpu_dp; | |
3290 | wmb(); | |
3291 | } else { | |
6ca80638 | 3292 | /* If the LAG DSA conduit has no ports left, migrate back all |
acc43b7b VO |
3293 | * user ports to the first physical CPU port |
3294 | */ | |
6ca80638 | 3295 | dsa_tree_migrate_ports_from_lag_conduit(dst, lag_dev); |
acc43b7b VO |
3296 | } |
3297 | ||
6ca80638 | 3298 | /* This DSA conduit has left its LAG in any case, so let |
acc43b7b VO |
3299 | * the CPU port leave the hardware LAG as well |
3300 | */ | |
6ca80638 | 3301 | dsa_conduit_lag_teardown(lag_dev, conduit->dsa_ptr); |
acc43b7b VO |
3302 | } |
3303 | ||
6ca80638 FF |
3304 | static int dsa_conduit_changeupper(struct net_device *dev, |
3305 | struct netdev_notifier_changeupper_info *info) | |
acc43b7b VO |
3306 | { |
3307 | struct netlink_ext_ack *extack; | |
3308 | int err = NOTIFY_DONE; | |
3309 | ||
3310 | if (!netdev_uses_dsa(dev)) | |
3311 | return err; | |
3312 | ||
3313 | extack = netdev_notifier_info_to_extack(&info->info); | |
3314 | ||
3315 | if (netif_is_lag_master(info->upper_dev)) { | |
3316 | if (info->linking) { | |
6ca80638 FF |
3317 | err = dsa_conduit_lag_join(dev, info->upper_dev, |
3318 | info->upper_info, extack); | |
acc43b7b VO |
3319 | err = notifier_from_errno(err); |
3320 | } else { | |
6ca80638 | 3321 | dsa_conduit_lag_leave(dev, info->upper_dev); |
acc43b7b VO |
3322 | err = NOTIFY_OK; |
3323 | } | |
3324 | } | |
3325 | ||
3326 | return err; | |
3327 | } | |
3328 | ||
6ca80638 FF |
3329 | static int dsa_user_netdevice_event(struct notifier_block *nb, |
3330 | unsigned long event, void *ptr) | |
6debb68a VD |
3331 | { |
3332 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | |
3333 | ||
83501299 | 3334 | switch (event) { |
2b138406 VO |
3335 | case NETDEV_PRECHANGEUPPER: { |
3336 | struct netdev_notifier_changeupper_info *info = ptr; | |
e358bef7 | 3337 | int err; |
2b138406 | 3338 | |
6ca80638 | 3339 | err = dsa_user_prechangeupper_sanity_check(dev, info); |
0498277e | 3340 | if (notifier_to_errno(err)) |
4ede74e7 | 3341 | return err; |
e358bef7 | 3342 | |
6ca80638 | 3343 | err = dsa_conduit_prechangeupper_sanity_check(dev, info); |
4f03dcc6 VO |
3344 | if (notifier_to_errno(err)) |
3345 | return err; | |
3346 | ||
6ca80638 | 3347 | err = dsa_lag_conduit_prechangelower_sanity_check(dev, info); |
acc43b7b VO |
3348 | if (notifier_to_errno(err)) |
3349 | return err; | |
3350 | ||
920a33cd VO |
3351 | err = dsa_bridge_prechangelower_sanity_check(dev, info); |
3352 | if (notifier_to_errno(err)) | |
3353 | return err; | |
3354 | ||
6ca80638 | 3355 | err = dsa_user_prechangeupper(dev, ptr); |
4c3f80d2 VO |
3356 | if (notifier_to_errno(err)) |
3357 | return err; | |
74918945 | 3358 | |
6ca80638 | 3359 | err = dsa_user_lag_prechangeupper(dev, ptr); |
4c3f80d2 VO |
3360 | if (notifier_to_errno(err)) |
3361 | return err; | |
74918945 | 3362 | |
83501299 | 3363 | break; |
2b138406 | 3364 | } |
4c3f80d2 VO |
3365 | case NETDEV_CHANGEUPPER: { |
3366 | int err; | |
3367 | ||
6ca80638 | 3368 | err = dsa_user_changeupper(dev, ptr); |
4c3f80d2 VO |
3369 | if (notifier_to_errno(err)) |
3370 | return err; | |
058102a6 | 3371 | |
6ca80638 | 3372 | err = dsa_user_lag_changeupper(dev, ptr); |
4c3f80d2 VO |
3373 | if (notifier_to_errno(err)) |
3374 | return err; | |
058102a6 | 3375 | |
6ca80638 | 3376 | err = dsa_conduit_changeupper(dev, ptr); |
acc43b7b VO |
3377 | if (notifier_to_errno(err)) |
3378 | return err; | |
3379 | ||
058102a6 | 3380 | break; |
4c3f80d2 | 3381 | } |
058102a6 TW |
3382 | case NETDEV_CHANGELOWERSTATE: { |
3383 | struct netdev_notifier_changelowerstate_info *info = ptr; | |
3384 | struct dsa_port *dp; | |
0a6d58a7 | 3385 | int err = 0; |
058102a6 | 3386 | |
6ca80638 FF |
3387 | if (dsa_user_dev_check(dev)) { |
3388 | dp = dsa_user_to_port(dev); | |
acc43b7b VO |
3389 | |
3390 | err = dsa_port_lag_change(dp, info->lower_state_info); | |
3391 | } | |
8e92ab3a | 3392 | |
6ca80638 | 3393 | /* Mirror LAG port events on DSA conduits that are in |
acc43b7b VO |
3394 | * a LAG towards their respective switch CPU ports |
3395 | */ | |
3396 | if (netdev_uses_dsa(dev)) { | |
3397 | dp = dev->dsa_ptr; | |
3398 | ||
3399 | err = dsa_port_lag_change(dp, info->lower_state_info); | |
3400 | } | |
058102a6 | 3401 | |
058102a6 TW |
3402 | return notifier_from_errno(err); |
3403 | } | |
295ab96f VO |
3404 | case NETDEV_CHANGE: |
3405 | case NETDEV_UP: { | |
6ca80638 FF |
3406 | /* Track state of conduit port. |
3407 | * DSA driver may require the conduit port (and indirectly | |
295ab96f VO |
3408 | * the tagger) to be available for some special operation. |
3409 | */ | |
3410 | if (netdev_uses_dsa(dev)) { | |
3411 | struct dsa_port *cpu_dp = dev->dsa_ptr; | |
3412 | struct dsa_switch_tree *dst = cpu_dp->ds->dst; | |
3413 | ||
6ca80638 FF |
3414 | /* Track when the conduit port is UP */ |
3415 | dsa_tree_conduit_oper_state_change(dst, dev, | |
3416 | netif_oper_up(dev)); | |
295ab96f | 3417 | |
6ca80638 | 3418 | /* Track when the conduit port is ready and can accept |
295ab96f VO |
3419 | * packet. |
3420 | * NETDEV_UP event is not enough to flag a port as ready. | |
3421 | * We also have to wait for linkwatch_do_dev to dev_activate | |
3422 | * and emit a NETDEV_CHANGE event. | |
6ca80638 | 3423 | * We check if a conduit port is ready by checking if the dev |
295ab96f VO |
3424 | * have a qdisc assigned and is not noop. |
3425 | */ | |
6ca80638 FF |
3426 | dsa_tree_conduit_admin_state_change(dst, dev, |
3427 | !qdisc_tx_is_noop(dev)); | |
295ab96f VO |
3428 | |
3429 | return NOTIFY_OK; | |
3430 | } | |
3431 | ||
3432 | return NOTIFY_DONE; | |
3433 | } | |
c0a8a9c2 VO |
3434 | case NETDEV_GOING_DOWN: { |
3435 | struct dsa_port *dp, *cpu_dp; | |
3436 | struct dsa_switch_tree *dst; | |
3437 | LIST_HEAD(close_list); | |
3438 | ||
3439 | if (!netdev_uses_dsa(dev)) | |
3440 | return NOTIFY_DONE; | |
3441 | ||
3442 | cpu_dp = dev->dsa_ptr; | |
3443 | dst = cpu_dp->ds->dst; | |
3444 | ||
6ca80638 | 3445 | dsa_tree_conduit_admin_state_change(dst, dev, false); |
295ab96f | 3446 | |
c0a8a9c2 | 3447 | list_for_each_entry(dp, &dst->ports, list) { |
d0004a02 | 3448 | if (!dsa_port_is_user(dp)) |
c0a8a9c2 VO |
3449 | continue; |
3450 | ||
7136097e VO |
3451 | if (dp->cpu_dp != cpu_dp) |
3452 | continue; | |
3453 | ||
6ca80638 | 3454 | list_add(&dp->user->close_list, &close_list); |
c0a8a9c2 VO |
3455 | } |
3456 | ||
3457 | dev_close_many(&close_list, true); | |
3458 | ||
3459 | return NOTIFY_OK; | |
3460 | } | |
3461 | default: | |
3462 | break; | |
cc1d5bda | 3463 | } |
b73adef6 | 3464 | |
b73adef6 FF |
3465 | return NOTIFY_DONE; |
3466 | } | |
88e4f0ca | 3467 | |
c4bb76a9 VO |
3468 | static void |
3469 | dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work) | |
3470 | { | |
c35b57ce | 3471 | struct switchdev_notifier_fdb_info info = {}; |
c4bb76a9 VO |
3472 | |
3473 | info.addr = switchdev_work->addr; | |
3474 | info.vid = switchdev_work->vid; | |
3475 | info.offloaded = true; | |
c4bb76a9 | 3476 | call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, |
93c79823 | 3477 | switchdev_work->orig_dev, &info.info, NULL); |
c4bb76a9 | 3478 | } |
c9eb3e0f | 3479 | |
6ca80638 | 3480 | static void dsa_user_switchdev_event_work(struct work_struct *work) |
c9eb3e0f AS |
3481 | { |
3482 | struct dsa_switchdev_event_work *switchdev_work = | |
3483 | container_of(work, struct dsa_switchdev_event_work, work); | |
68d6d71e | 3484 | const unsigned char *addr = switchdev_work->addr; |
e35f12e9 | 3485 | struct net_device *dev = switchdev_work->dev; |
68d6d71e | 3486 | u16 vid = switchdev_work->vid; |
e35f12e9 | 3487 | struct dsa_switch *ds; |
c4bb76a9 | 3488 | struct dsa_port *dp; |
c9eb3e0f AS |
3489 | int err; |
3490 | ||
6ca80638 | 3491 | dp = dsa_user_to_port(dev); |
e35f12e9 | 3492 | ds = dp->ds; |
c4bb76a9 | 3493 | |
c9eb3e0f AS |
3494 | switch (switchdev_work->event) { |
3495 | case SWITCHDEV_FDB_ADD_TO_DEVICE: | |
3dc80afc | 3496 | if (switchdev_work->host_addr) |
68d6d71e | 3497 | err = dsa_port_bridge_host_fdb_add(dp, addr, vid); |
e212fa7c | 3498 | else if (dp->lag) |
68d6d71e | 3499 | err = dsa_port_lag_fdb_add(dp, addr, vid); |
3dc80afc | 3500 | else |
68d6d71e | 3501 | err = dsa_port_fdb_add(dp, addr, vid); |
c9eb3e0f | 3502 | if (err) { |
c4bb76a9 VO |
3503 | dev_err(ds->dev, |
3504 | "port %d failed to add %pM vid %d to fdb: %d\n", | |
68d6d71e | 3505 | dp->index, addr, vid, err); |
c9eb3e0f AS |
3506 | break; |
3507 | } | |
c4bb76a9 | 3508 | dsa_fdb_offload_notify(switchdev_work); |
c9eb3e0f AS |
3509 | break; |
3510 | ||
3511 | case SWITCHDEV_FDB_DEL_TO_DEVICE: | |
3dc80afc | 3512 | if (switchdev_work->host_addr) |
68d6d71e | 3513 | err = dsa_port_bridge_host_fdb_del(dp, addr, vid); |
e212fa7c | 3514 | else if (dp->lag) |
68d6d71e | 3515 | err = dsa_port_lag_fdb_del(dp, addr, vid); |
3dc80afc | 3516 | else |
68d6d71e | 3517 | err = dsa_port_fdb_del(dp, addr, vid); |
c9eb3e0f | 3518 | if (err) { |
c4bb76a9 VO |
3519 | dev_err(ds->dev, |
3520 | "port %d failed to delete %pM vid %d from fdb: %d\n", | |
68d6d71e | 3521 | dp->index, addr, vid, err); |
c9eb3e0f | 3522 | } |
2fd18650 | 3523 | |
c9eb3e0f AS |
3524 | break; |
3525 | } | |
c9eb3e0f | 3526 | |
c9eb3e0f | 3527 | kfree(switchdev_work); |
c9eb3e0f AS |
3528 | } |
3529 | ||
b94dc99c VO |
3530 | static bool dsa_foreign_dev_check(const struct net_device *dev, |
3531 | const struct net_device *foreign_dev) | |
d5f19486 | 3532 | { |
6ca80638 | 3533 | const struct dsa_port *dp = dsa_user_to_port(dev); |
b94dc99c | 3534 | struct dsa_switch_tree *dst = dp->ds->dst; |
d5f19486 | 3535 | |
b94dc99c | 3536 | if (netif_is_bridge_master(foreign_dev)) |
936db8a2 | 3537 | return !dsa_tree_offloads_bridge_dev(dst, foreign_dev); |
b94dc99c VO |
3538 | |
3539 | if (netif_is_bridge_port(foreign_dev)) | |
3540 | return !dsa_tree_offloads_bridge_port(dst, foreign_dev); | |
3541 | ||
3542 | /* Everything else is foreign */ | |
3543 | return true; | |
d5f19486 VO |
3544 | } |
3545 | ||
6ca80638 FF |
3546 | static int dsa_user_fdb_event(struct net_device *dev, |
3547 | struct net_device *orig_dev, | |
3548 | unsigned long event, const void *ctx, | |
3549 | const struct switchdev_notifier_fdb_info *fdb_info) | |
d5f19486 | 3550 | { |
b94dc99c | 3551 | struct dsa_switchdev_event_work *switchdev_work; |
6ca80638 | 3552 | struct dsa_port *dp = dsa_user_to_port(dev); |
b94dc99c VO |
3553 | bool host_addr = fdb_info->is_local; |
3554 | struct dsa_switch *ds = dp->ds; | |
3555 | ||
3556 | if (ctx && ctx != dp) | |
3557 | return 0; | |
3558 | ||
a860352e TW |
3559 | if (!dp->bridge) |
3560 | return 0; | |
3561 | ||
e212fa7c VO |
3562 | if (switchdev_fdb_is_dynamically_learned(fdb_info)) { |
3563 | if (dsa_port_offloads_bridge_port(dp, orig_dev)) | |
3564 | return 0; | |
b94dc99c | 3565 | |
e212fa7c VO |
3566 | /* FDB entries learned by the software bridge or by foreign |
3567 | * bridge ports should be installed as host addresses only if | |
3568 | * the driver requests assisted learning. | |
3569 | */ | |
3570 | if (!ds->assisted_learning_on_cpu_port) | |
3571 | return 0; | |
3572 | } | |
b94dc99c VO |
3573 | |
3574 | /* Also treat FDB entries on foreign interfaces bridged with us as host | |
3575 | * addresses. | |
3576 | */ | |
3577 | if (dsa_foreign_dev_check(dev, orig_dev)) | |
3578 | host_addr = true; | |
3579 | ||
e212fa7c VO |
3580 | /* Check early that we're not doing work in vain. |
3581 | * Host addresses on LAG ports still require regular FDB ops, | |
3582 | * since the CPU port isn't in a LAG. | |
3583 | */ | |
3584 | if (dp->lag && !host_addr) { | |
3585 | if (!ds->ops->lag_fdb_add || !ds->ops->lag_fdb_del) | |
3586 | return -EOPNOTSUPP; | |
3587 | } else { | |
3588 | if (!ds->ops->port_fdb_add || !ds->ops->port_fdb_del) | |
3589 | return -EOPNOTSUPP; | |
3590 | } | |
3591 | ||
b94dc99c VO |
3592 | switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); |
3593 | if (!switchdev_work) | |
3594 | return -ENOMEM; | |
3595 | ||
3596 | netdev_dbg(dev, "%s FDB entry towards %s, addr %pM vid %d%s\n", | |
3597 | event == SWITCHDEV_FDB_ADD_TO_DEVICE ? "Adding" : "Deleting", | |
3598 | orig_dev->name, fdb_info->addr, fdb_info->vid, | |
3599 | host_addr ? " as host address" : ""); | |
d5f19486 | 3600 | |
6ca80638 | 3601 | INIT_WORK(&switchdev_work->work, dsa_user_switchdev_event_work); |
b94dc99c VO |
3602 | switchdev_work->event = event; |
3603 | switchdev_work->dev = dev; | |
93c79823 | 3604 | switchdev_work->orig_dev = orig_dev; |
d5f19486 | 3605 | |
b94dc99c VO |
3606 | ether_addr_copy(switchdev_work->addr, fdb_info->addr); |
3607 | switchdev_work->vid = fdb_info->vid; | |
3608 | switchdev_work->host_addr = host_addr; | |
3609 | ||
b94dc99c VO |
3610 | dsa_schedule_work(&switchdev_work->work); |
3611 | ||
3612 | return 0; | |
3613 | } | |
3614 | ||
c9eb3e0f | 3615 | /* Called under rcu_read_lock() */ |
6ca80638 FF |
3616 | static int dsa_user_switchdev_event(struct notifier_block *unused, |
3617 | unsigned long event, void *ptr) | |
c9eb3e0f AS |
3618 | { |
3619 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); | |
79b139f4 VD |
3620 | int err; |
3621 | ||
447d290a VO |
3622 | switch (event) { |
3623 | case SWITCHDEV_PORT_ATTR_SET: | |
79b139f4 | 3624 | err = switchdev_handle_port_attr_set(dev, ptr, |
6ca80638 FF |
3625 | dsa_user_dev_check, |
3626 | dsa_user_port_attr_set); | |
79b139f4 | 3627 | return notifier_from_errno(err); |
447d290a VO |
3628 | case SWITCHDEV_FDB_ADD_TO_DEVICE: |
3629 | case SWITCHDEV_FDB_DEL_TO_DEVICE: | |
716a30a9 | 3630 | err = switchdev_handle_fdb_event_to_device(dev, event, ptr, |
6ca80638 | 3631 | dsa_user_dev_check, |
716a30a9 | 3632 | dsa_foreign_dev_check, |
6ca80638 | 3633 | dsa_user_fdb_event); |
b94dc99c | 3634 | return notifier_from_errno(err); |
c9eb3e0f | 3635 | default: |
c9eb3e0f AS |
3636 | return NOTIFY_DONE; |
3637 | } | |
3638 | ||
c9eb3e0f | 3639 | return NOTIFY_OK; |
c9eb3e0f AS |
3640 | } |
3641 | ||
6ca80638 FF |
3642 | static int dsa_user_switchdev_blocking_event(struct notifier_block *unused, |
3643 | unsigned long event, void *ptr) | |
2b239f67 PM |
3644 | { |
3645 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); | |
79b139f4 | 3646 | int err; |
2b239f67 PM |
3647 | |
3648 | switch (event) { | |
79b139f4 | 3649 | case SWITCHDEV_PORT_OBJ_ADD: |
164f861b | 3650 | err = switchdev_handle_port_obj_add_foreign(dev, ptr, |
6ca80638 | 3651 | dsa_user_dev_check, |
164f861b | 3652 | dsa_foreign_dev_check, |
6ca80638 | 3653 | dsa_user_port_obj_add); |
79b139f4 | 3654 | return notifier_from_errno(err); |
2b239f67 | 3655 | case SWITCHDEV_PORT_OBJ_DEL: |
164f861b | 3656 | err = switchdev_handle_port_obj_del_foreign(dev, ptr, |
6ca80638 | 3657 | dsa_user_dev_check, |
164f861b | 3658 | dsa_foreign_dev_check, |
6ca80638 | 3659 | dsa_user_port_obj_del); |
79b139f4 | 3660 | return notifier_from_errno(err); |
9ed1eced | 3661 | case SWITCHDEV_PORT_ATTR_SET: |
79b139f4 | 3662 | err = switchdev_handle_port_attr_set(dev, ptr, |
6ca80638 FF |
3663 | dsa_user_dev_check, |
3664 | dsa_user_port_attr_set); | |
79b139f4 | 3665 | return notifier_from_errno(err); |
2b239f67 PM |
3666 | } |
3667 | ||
3668 | return NOTIFY_DONE; | |
3669 | } | |
3670 | ||
6ca80638 FF |
3671 | static struct notifier_block dsa_user_nb __read_mostly = { |
3672 | .notifier_call = dsa_user_netdevice_event, | |
c9eb3e0f AS |
3673 | }; |
3674 | ||
6ca80638 FF |
3675 | struct notifier_block dsa_user_switchdev_notifier = { |
3676 | .notifier_call = dsa_user_switchdev_event, | |
88e4f0ca VD |
3677 | }; |
3678 | ||
6ca80638 FF |
3679 | struct notifier_block dsa_user_switchdev_blocking_notifier = { |
3680 | .notifier_call = dsa_user_switchdev_blocking_event, | |
2b239f67 PM |
3681 | }; |
3682 | ||
6ca80638 | 3683 | int dsa_user_register_notifier(void) |
88e4f0ca | 3684 | { |
2b239f67 | 3685 | struct notifier_block *nb; |
c9eb3e0f AS |
3686 | int err; |
3687 | ||
6ca80638 | 3688 | err = register_netdevice_notifier(&dsa_user_nb); |
c9eb3e0f AS |
3689 | if (err) |
3690 | return err; | |
3691 | ||
6ca80638 | 3692 | err = register_switchdev_notifier(&dsa_user_switchdev_notifier); |
c9eb3e0f AS |
3693 | if (err) |
3694 | goto err_switchdev_nb; | |
3695 | ||
6ca80638 | 3696 | nb = &dsa_user_switchdev_blocking_notifier; |
2b239f67 PM |
3697 | err = register_switchdev_blocking_notifier(nb); |
3698 | if (err) | |
3699 | goto err_switchdev_blocking_nb; | |
3700 | ||
c9eb3e0f AS |
3701 | return 0; |
3702 | ||
2b239f67 | 3703 | err_switchdev_blocking_nb: |
6ca80638 | 3704 | unregister_switchdev_notifier(&dsa_user_switchdev_notifier); |
c9eb3e0f | 3705 | err_switchdev_nb: |
6ca80638 | 3706 | unregister_netdevice_notifier(&dsa_user_nb); |
c9eb3e0f | 3707 | return err; |
88e4f0ca VD |
3708 | } |
3709 | ||
6ca80638 | 3710 | void dsa_user_unregister_notifier(void) |
88e4f0ca | 3711 | { |
2b239f67 | 3712 | struct notifier_block *nb; |
88e4f0ca VD |
3713 | int err; |
3714 | ||
6ca80638 | 3715 | nb = &dsa_user_switchdev_blocking_notifier; |
2b239f67 PM |
3716 | err = unregister_switchdev_blocking_notifier(nb); |
3717 | if (err) | |
3718 | pr_err("DSA: failed to unregister switchdev blocking notifier (%d)\n", err); | |
3719 | ||
6ca80638 | 3720 | err = unregister_switchdev_notifier(&dsa_user_switchdev_notifier); |
c9eb3e0f AS |
3721 | if (err) |
3722 | pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err); | |
3723 | ||
6ca80638 | 3724 | err = unregister_netdevice_notifier(&dsa_user_nb); |
88e4f0ca | 3725 | if (err) |
6ca80638 | 3726 | pr_err("DSA: failed to unregister user notifier (%d)\n", err); |
88e4f0ca | 3727 | } |