Blame view

net/dsa/slave.c 43.1 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
91da11f87   Lennert Buytenhek   net: Distributed ...
2
3
  /*
   * net/dsa/slave.c - Slave device handling
e84665c9c   Lennert Buytenhek   dsa: add switch c...
4
   * Copyright (c) 2008-2009 Marvell Semiconductor
91da11f87   Lennert Buytenhek   net: Distributed ...
5
6
7
   */
  
  #include <linux/list.h>
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
8
  #include <linux/etherdevice.h>
b73adef67   Florian Fainelli   net: dsa: integra...
9
  #include <linux/netdevice.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
10
  #include <linux/phy.h>
a28205437   Florian Fainelli   net: dsa: add inc...
11
  #include <linux/phy_fixed.h>
aab9c4067   Florian Fainelli   net: dsa: Plug in...
12
  #include <linux/phylink.h>
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
13
14
  #include <linux/of_net.h>
  #include <linux/of_mdio.h>
7f854420f   Andrew Lunn   phy: Add API for ...
15
  #include <linux/mdio.h>
b73adef67   Florian Fainelli   net: dsa: integra...
16
  #include <net/rtnetlink.h>
f50f21274   Florian Fainelli   net: dsa: Add plu...
17
18
  #include <net/pkt_cls.h>
  #include <net/tc_act/tc_mirred.h>
b73adef67   Florian Fainelli   net: dsa: integra...
19
  #include <linux/if_bridge.h>
04ff53f96   Florian Fainelli   net: dsa: Add net...
20
  #include <linux/netpoll.h>
90af1059c   Brandon Streiff   net: dsa: forward...
21
  #include <linux/ptp_classify.h>
ea5dd34be   Vivien Didelot   net: dsa: include...
22

91da11f87   Lennert Buytenhek   net: Distributed ...
23
  #include "dsa_priv.h"
f3b78049d   Vivien Didelot   net: dsa: make ds...
24
  static bool dsa_slave_dev_check(const struct net_device *dev);
f50f21274   Florian Fainelli   net: dsa: Add plu...
25

91da11f87   Lennert Buytenhek   net: Distributed ...
26
27
28
29
  /* slave mii_bus handling ***************************************************/
  static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
  {
  	struct dsa_switch *ds = bus->priv;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
30
  	if (ds->phys_mii_mask & (1 << addr))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
31
  		return ds->ops->phy_read(ds, addr, reg);
91da11f87   Lennert Buytenhek   net: Distributed ...
32
33
34
35
36
37
38
  
  	return 0xffff;
  }
  
  static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
  {
  	struct dsa_switch *ds = bus->priv;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
39
  	if (ds->phys_mii_mask & (1 << addr))
9d490b4ee   Vivien Didelot   net: dsa: rename ...
40
  		return ds->ops->phy_write(ds, addr, reg, val);
91da11f87   Lennert Buytenhek   net: Distributed ...
41
42
43
44
45
46
47
48
49
50
  
  	return 0;
  }
  
  void dsa_slave_mii_bus_init(struct dsa_switch *ds)
  {
  	ds->slave_mii_bus->priv = (void *)ds;
  	ds->slave_mii_bus->name = "dsa slave smi";
  	ds->slave_mii_bus->read = dsa_slave_phy_read;
  	ds->slave_mii_bus->write = dsa_slave_phy_write;
0b7b498d4   Florian Fainelli   net: dsa: Provide...
51
  	snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d",
49463b7f2   Vivien Didelot   net: dsa: make tr...
52
  		 ds->dst->index, ds->index);
c33063d6a   Andrew Lunn   dsa: Remove maste...
53
  	ds->slave_mii_bus->parent = ds->dev;
24df8986f   Vivien Didelot   net: dsa: set sla...
54
  	ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
91da11f87   Lennert Buytenhek   net: Distributed ...
55
56
57
58
  }
  
  
  /* slave device handling ****************************************************/
abd2be00d   Nicolas Dichtel   dsa: implement nd...
59
  static int dsa_slave_get_iflink(const struct net_device *dev)
c08408015   Lennert Buytenhek   dsa: set ->iflink...
60
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
61
  	return dsa_slave_to_master(dev)->ifindex;
c08408015   Lennert Buytenhek   dsa: set ->iflink...
62
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
63
64
  static int dsa_slave_open(struct net_device *dev)
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
65
  	struct net_device *master = dsa_slave_to_master(dev);
d945097bb   Vivien Didelot   net: dsa: add sla...
66
  	struct dsa_port *dp = dsa_slave_to_port(dev);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
67
68
69
70
  	int err;
  
  	if (!(master->flags & IFF_UP))
  		return -ENETDOWN;
8feedbb4a   Joe Perches   dsa: Convert comp...
71
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
a748ee242   Jiri Pirko   net: move address...
72
  		err = dev_uc_add(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  		if (err < 0)
  			goto out;
  	}
  
  	if (dev->flags & IFF_ALLMULTI) {
  		err = dev_set_allmulti(master, 1);
  		if (err < 0)
  			goto del_unicast;
  	}
  	if (dev->flags & IFF_PROMISC) {
  		err = dev_set_promiscuity(master, 1);
  		if (err < 0)
  			goto clear_allmulti;
  	}
0115dcd17   Vivien Didelot   net: dsa: use sla...
87
  	err = dsa_port_enable(dp, dev->phydev);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
88
89
  	if (err)
  		goto clear_promisc;
b73adef67   Florian Fainelli   net: dsa: integra...
90

aab9c4067   Florian Fainelli   net: dsa: Plug in...
91
  	phylink_start(dp->pl);
f7f1de51e   Florian Fainelli   net: dsa: start a...
92

91da11f87   Lennert Buytenhek   net: Distributed ...
93
  	return 0;
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
94

b2f2af21e   Florian Fainelli   net: dsa: allow e...
95
96
  clear_promisc:
  	if (dev->flags & IFF_PROMISC)
4fdeddfe0   Gilad Ben-Yossef   dsa: fix promiscu...
97
  		dev_set_promiscuity(master, -1);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
98
99
100
101
  clear_allmulti:
  	if (dev->flags & IFF_ALLMULTI)
  		dev_set_allmulti(master, -1);
  del_unicast:
8feedbb4a   Joe Perches   dsa: Convert comp...
102
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
103
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
104
105
  out:
  	return err;
91da11f87   Lennert Buytenhek   net: Distributed ...
106
107
108
109
  }
  
  static int dsa_slave_close(struct net_device *dev)
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
110
  	struct net_device *master = dsa_slave_to_master(dev);
d945097bb   Vivien Didelot   net: dsa: add sla...
111
  	struct dsa_port *dp = dsa_slave_to_port(dev);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
112

97a69a0de   Vladimir Oltean   net: dsa: Add sup...
113
114
  	cancel_work_sync(&dp->xmit_work);
  	skb_queue_purge(&dp->xmit_queue);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
115
  	phylink_stop(dp->pl);
f7f1de51e   Florian Fainelli   net: dsa: start a...
116

75104db0c   Andrew Lunn   dsa: Remove phyde...
117
  	dsa_port_disable(dp);
6457edfe7   Vivien Didelot   net: dsa: make sl...
118

df02c6ff2   Lennert Buytenhek   dsa: fix master i...
119
  	dev_mc_unsync(master, dev);
a748ee242   Jiri Pirko   net: move address...
120
  	dev_uc_unsync(master, dev);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
121
122
123
124
  	if (dev->flags & IFF_ALLMULTI)
  		dev_set_allmulti(master, -1);
  	if (dev->flags & IFF_PROMISC)
  		dev_set_promiscuity(master, -1);
8feedbb4a   Joe Perches   dsa: Convert comp...
125
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
126
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
127

91da11f87   Lennert Buytenhek   net: Distributed ...
128
129
130
131
132
  	return 0;
  }
  
  static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
133
  	struct net_device *master = dsa_slave_to_master(dev);
17ab4f61b   Rundong Ge   net: dsa: slave: ...
134
135
136
137
138
139
140
141
  	if (dev->flags & IFF_UP) {
  		if (change & IFF_ALLMULTI)
  			dev_set_allmulti(master,
  					 dev->flags & IFF_ALLMULTI ? 1 : -1);
  		if (change & IFF_PROMISC)
  			dev_set_promiscuity(master,
  					    dev->flags & IFF_PROMISC ? 1 : -1);
  	}
91da11f87   Lennert Buytenhek   net: Distributed ...
142
143
144
145
  }
  
  static void dsa_slave_set_rx_mode(struct net_device *dev)
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
146
  	struct net_device *master = dsa_slave_to_master(dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
147
148
  
  	dev_mc_sync(master, dev);
a748ee242   Jiri Pirko   net: move address...
149
  	dev_uc_sync(master, dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
150
  }
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
151
  static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
91da11f87   Lennert Buytenhek   net: Distributed ...
152
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
153
  	struct net_device *master = dsa_slave_to_master(dev);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
154
155
156
157
158
159
160
161
  	struct sockaddr *addr = a;
  	int err;
  
  	if (!is_valid_ether_addr(addr->sa_data))
  		return -EADDRNOTAVAIL;
  
  	if (!(dev->flags & IFF_UP))
  		goto out;
8feedbb4a   Joe Perches   dsa: Convert comp...
162
  	if (!ether_addr_equal(addr->sa_data, master->dev_addr)) {
a748ee242   Jiri Pirko   net: move address...
163
  		err = dev_uc_add(master, addr->sa_data);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
164
165
166
  		if (err < 0)
  			return err;
  	}
8feedbb4a   Joe Perches   dsa: Convert comp...
167
  	if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
a748ee242   Jiri Pirko   net: move address...
168
  		dev_uc_del(master, dev->dev_addr);
df02c6ff2   Lennert Buytenhek   dsa: fix master i...
169
170
  
  out:
d08f161a1   Joe Perches   dsa: Use ether_ad...
171
  	ether_addr_copy(dev->dev_addr, addr->sa_data);
91da11f87   Lennert Buytenhek   net: Distributed ...
172
173
174
  
  	return 0;
  }
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  struct dsa_slave_dump_ctx {
  	struct net_device *dev;
  	struct sk_buff *skb;
  	struct netlink_callback *cb;
  	int idx;
  };
  
  static int
  dsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid,
  			   bool is_static, void *data)
  {
  	struct dsa_slave_dump_ctx *dump = data;
  	u32 portid = NETLINK_CB(dump->cb->skb).portid;
  	u32 seq = dump->cb->nlh->nlmsg_seq;
  	struct nlmsghdr *nlh;
  	struct ndmsg *ndm;
  
  	if (dump->idx < dump->cb->args[2])
  		goto skip;
  
  	nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
  			sizeof(*ndm), NLM_F_MULTI);
  	if (!nlh)
  		return -EMSGSIZE;
  
  	ndm = nlmsg_data(nlh);
  	ndm->ndm_family  = AF_BRIDGE;
  	ndm->ndm_pad1    = 0;
  	ndm->ndm_pad2    = 0;
  	ndm->ndm_flags   = NTF_SELF;
  	ndm->ndm_type    = 0;
  	ndm->ndm_ifindex = dump->dev->ifindex;
  	ndm->ndm_state   = is_static ? NUD_NOARP : NUD_REACHABLE;
  
  	if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
  		goto nla_put_failure;
  
  	if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
  		goto nla_put_failure;
  
  	nlmsg_end(dump->skb, nlh);
  
  skip:
  	dump->idx++;
  	return 0;
  
  nla_put_failure:
  	nlmsg_cancel(dump->skb, nlh);
  	return -EMSGSIZE;
  }
  
  static int
  dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
  		   struct net_device *dev, struct net_device *filter_dev,
  		   int *idx)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
231
  	struct dsa_port *dp = dsa_slave_to_port(dev);
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
232
233
234
235
236
237
  	struct dsa_slave_dump_ctx dump = {
  		.dev = dev,
  		.skb = skb,
  		.cb = cb,
  		.idx = *idx,
  	};
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
238
  	int err;
de40fc5d2   Vivien Didelot   net: dsa: add por...
239
  	err = dsa_port_fdb_dump(dp, dsa_slave_port_fdb_do_dump, &dump);
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
240
  	*idx = dump.idx;
de40fc5d2   Vivien Didelot   net: dsa: add por...
241

2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
242
243
  	return err;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
244
245
  static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
  {
0336369d3   Brandon Streiff   net: dsa: forward...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->dp->ds;
  	int port = p->dp->index;
  
  	/* Pass through to switch driver if it supports timestamping */
  	switch (cmd) {
  	case SIOCGHWTSTAMP:
  		if (ds->ops->port_hwtstamp_get)
  			return ds->ops->port_hwtstamp_get(ds, port, ifr);
  		break;
  	case SIOCSHWTSTAMP:
  		if (ds->ops->port_hwtstamp_set)
  			return ds->ops->port_hwtstamp_set(ds, port, ifr);
  		break;
  	}
aab9c4067   Florian Fainelli   net: dsa: Plug in...
261
  	return phylink_mii_ioctl(p->dp->pl, ifr, cmd);
91da11f87   Lennert Buytenhek   net: Distributed ...
262
  }
356360625   Scott Feldman   switchdev: conver...
263
  static int dsa_slave_port_attr_set(struct net_device *dev,
f7fadf304   Jiri Pirko   switchdev: make s...
264
  				   const struct switchdev_attr *attr,
7ea6eb3f5   Jiri Pirko   switchdev: introd...
265
  				   struct switchdev_trans *trans)
356360625   Scott Feldman   switchdev: conver...
266
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
267
  	struct dsa_port *dp = dsa_slave_to_port(dev);
b8d866ac6   Vivien Didelot   net: dsa: fix pre...
268
  	int ret;
356360625   Scott Feldman   switchdev: conver...
269
270
  
  	switch (attr->id) {
1f8683987   Jiri Pirko   switchdev: rename...
271
  	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
fd3645413   Vivien Didelot   net: dsa: change ...
272
  		ret = dsa_port_set_state(dp, attr->u.stp_state, trans);
356360625   Scott Feldman   switchdev: conver...
273
  		break;
fb2dabad6   Vivien Didelot   net: dsa: support...
274
  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
c02c4175c   Vivien Didelot   net: dsa: change ...
275
276
  		ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
  					      trans);
fb2dabad6   Vivien Didelot   net: dsa: support...
277
  		break;
34a79f63b   Vivien Didelot   net: dsa: support...
278
  	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
072bb1903   Vivien Didelot   net: dsa: change ...
279
  		ret = dsa_port_ageing_time(dp, attr->u.ageing_time, trans);
34a79f63b   Vivien Didelot   net: dsa: support...
280
  		break;
ea87005a0   Florian Fainelli   net: dsa: Add set...
281
282
283
284
  	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
  		ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
  						trans);
  		break;
57652796a   Russell King   net: dsa: add sup...
285
286
287
  	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
  		ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans);
  		break;
08cc83cc7   Vivien Didelot   net: dsa: add sup...
288
289
290
  	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
  		ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans);
  		break;
356360625   Scott Feldman   switchdev: conver...
291
292
293
294
295
296
297
  	default:
  		ret = -EOPNOTSUPP;
  		break;
  	}
  
  	return ret;
  }
bdcff080f   Vivien Didelot   net: dsa: add sla...
298
299
300
301
302
303
304
305
306
307
  static int dsa_slave_vlan_add(struct net_device *dev,
  			      const struct switchdev_obj *obj,
  			      struct switchdev_trans *trans)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct switchdev_obj_port_vlan vlan;
  	int err;
  
  	if (obj->orig_dev != dev)
  		return -EOPNOTSUPP;
c5335d737   Vivien Didelot   net: dsa: check b...
308
309
  	if (dp->bridge_dev && !br_vlan_enabled(dp->bridge_dev))
  		return 0;
bdcff080f   Vivien Didelot   net: dsa: add sla...
310
311
312
313
314
  	vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj);
  
  	err = dsa_port_vlan_add(dp, &vlan, trans);
  	if (err)
  		return err;
b9499904f   Vivien Didelot   net: dsa: clear V...
315
316
317
318
319
  	/* We need the dedicated CPU port to be a member of the VLAN as well.
  	 * Even though drivers often handle CPU membership in special ways,
  	 * it doesn't make sense to program a PVID, so clear this flag.
  	 */
  	vlan.flags &= ~BRIDGE_VLAN_INFO_PVID;
7e1741b47   Vivien Didelot   net: dsa: program...
320
321
322
  	err = dsa_port_vlan_add(dp->cpu_dp, &vlan, trans);
  	if (err)
  		return err;
bdcff080f   Vivien Didelot   net: dsa: add sla...
323
324
  	return 0;
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
325
  static int dsa_slave_port_obj_add(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
326
  				  const struct switchdev_obj *obj,
79b139f4b   Vivien Didelot   net: dsa: use swi...
327
328
  				  struct switchdev_trans *trans,
  				  struct netlink_ext_ack *extack)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
329
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
330
  	struct dsa_port *dp = dsa_slave_to_port(dev);
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
331
332
333
334
335
336
  	int err;
  
  	/* For the prepare phase, ensure the full set of changes is feasable in
  	 * one go in order to signal a failure properly. If an operation is not
  	 * supported, return -EOPNOTSUPP.
  	 */
9e8f4a548   Jiri Pirko   switchdev: push o...
337
  	switch (obj->id) {
8df302552   Vivien Didelot   net: dsa: add MDB...
338
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
79b139f4b   Vivien Didelot   net: dsa: use swi...
339
340
  		if (obj->orig_dev != dev)
  			return -EOPNOTSUPP;
bcebb976e   Vivien Didelot   net: dsa: change ...
341
  		err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans);
8df302552   Vivien Didelot   net: dsa: add MDB...
342
  		break;
5f4dbc50c   Andrew Lunn   net: dsa: slave: ...
343
344
345
346
347
348
349
  	case SWITCHDEV_OBJ_ID_HOST_MDB:
  		/* DSA can directly translate this to a normal MDB add,
  		 * but on the CPU port.
  		 */
  		err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj),
  				       trans);
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
350
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
bdcff080f   Vivien Didelot   net: dsa: add sla...
351
  		err = dsa_slave_vlan_add(dev, obj, trans);
111495361   Vivien Didelot   net: dsa: add sup...
352
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
353
354
355
356
357
358
359
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
bdcff080f   Vivien Didelot   net: dsa: add sla...
360
361
362
363
364
365
366
  static int dsa_slave_vlan_del(struct net_device *dev,
  			      const struct switchdev_obj *obj)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	if (obj->orig_dev != dev)
  		return -EOPNOTSUPP;
c5335d737   Vivien Didelot   net: dsa: check b...
367
368
  	if (dp->bridge_dev && !br_vlan_enabled(dp->bridge_dev))
  		return 0;
7e1741b47   Vivien Didelot   net: dsa: program...
369
370
371
  	/* Do not deprogram the CPU port as it may be shared with other user
  	 * ports which can be members of this VLAN as well.
  	 */
bdcff080f   Vivien Didelot   net: dsa: add sla...
372
373
  	return dsa_port_vlan_del(dp, SWITCHDEV_OBJ_PORT_VLAN(obj));
  }
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
374
  static int dsa_slave_port_obj_del(struct net_device *dev,
648b4a995   Jiri Pirko   switchdev: bring ...
375
  				  const struct switchdev_obj *obj)
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
376
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
377
  	struct dsa_port *dp = dsa_slave_to_port(dev);
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
378
  	int err;
9e8f4a548   Jiri Pirko   switchdev: push o...
379
  	switch (obj->id) {
8df302552   Vivien Didelot   net: dsa: add MDB...
380
  	case SWITCHDEV_OBJ_ID_PORT_MDB:
79b139f4b   Vivien Didelot   net: dsa: use swi...
381
382
  		if (obj->orig_dev != dev)
  			return -EOPNOTSUPP;
bcebb976e   Vivien Didelot   net: dsa: change ...
383
  		err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
8df302552   Vivien Didelot   net: dsa: add MDB...
384
  		break;
5f4dbc50c   Andrew Lunn   net: dsa: slave: ...
385
386
387
388
389
390
  	case SWITCHDEV_OBJ_ID_HOST_MDB:
  		/* DSA can directly translate this to a normal MDB add,
  		 * but on the CPU port.
  		 */
  		err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
  		break;
57d80838d   Jiri Pirko   switchdev: rename...
391
  	case SWITCHDEV_OBJ_ID_PORT_VLAN:
bdcff080f   Vivien Didelot   net: dsa: add sla...
392
  		err = dsa_slave_vlan_del(dev, obj);
111495361   Vivien Didelot   net: dsa: add sup...
393
  		break;
ba14d9eb1   Vivien Didelot   net: dsa: add sup...
394
395
396
397
398
399
400
  	default:
  		err = -EOPNOTSUPP;
  		break;
  	}
  
  	return err;
  }
929d6c145   Florian Fainelli   net: dsa: Impleme...
401
402
  static int dsa_slave_get_port_parent_id(struct net_device *dev,
  					struct netdev_phys_item_id *ppid)
b73adef67   Florian Fainelli   net: dsa: integra...
403
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
404
405
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
a42c8e33f   Andrew Lunn   net: dsa: Fix SWI...
406
  	struct dsa_switch_tree *dst = ds->dst;
b73adef67   Florian Fainelli   net: dsa: integra...
407

15b04acee   Jiri Pirko   dsa: pass switch ...
408
409
410
411
412
413
  	/* For non-legacy ports, devlink is used and it takes
  	 * care of the name generation. This ndo implementation
  	 * should be removed with legacy support.
  	 */
  	if (dp->ds->devlink)
  		return -EOPNOTSUPP;
929d6c145   Florian Fainelli   net: dsa: Impleme...
414
415
416
417
418
  	ppid->id_len = sizeof(dst->index);
  	memcpy(&ppid->id, &dst->index, ppid->id_len);
  
  	return 0;
  }
4fa7b7188   Vivien Didelot   net: dsa: better ...
419
420
  static inline netdev_tx_t dsa_slave_netpoll_send_skb(struct net_device *dev,
  						     struct sk_buff *skb)
04ff53f96   Florian Fainelli   net: dsa: Add net...
421
422
  {
  #ifdef CONFIG_NET_POLL_CONTROLLER
4fa7b7188   Vivien Didelot   net: dsa: better ...
423
  	struct dsa_slave_priv *p = netdev_priv(dev);
04ff53f96   Florian Fainelli   net: dsa: Add net...
424
425
426
427
428
429
430
  	if (p->netpoll)
  		netpoll_send_skb(p->netpoll, skb);
  #else
  	BUG();
  #endif
  	return NETDEV_TX_OK;
  }
90af1059c   Brandon Streiff   net: dsa: forward...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p,
  				 struct sk_buff *skb)
  {
  	struct dsa_switch *ds = p->dp->ds;
  	struct sk_buff *clone;
  	unsigned int type;
  
  	type = ptp_classify_raw(skb);
  	if (type == PTP_CLASS_NONE)
  		return;
  
  	if (!ds->ops->port_txtstamp)
  		return;
  
  	clone = skb_clone_sk(skb);
  	if (!clone)
  		return;
146d442c2   Vladimir Oltean   net: dsa: Keep a ...
448
  	DSA_SKB_CB(skb)->clone = clone;
90af1059c   Brandon Streiff   net: dsa: forward...
449
450
451
452
453
  	if (ds->ops->port_txtstamp(ds, p->dp->index, clone, type))
  		return;
  
  	kfree_skb(clone);
  }
97a69a0de   Vladimir Oltean   net: dsa: Add sup...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev)
  {
  	/* SKB for netpoll still need to be mangled with the protocol-specific
  	 * tag to be successfully transmitted
  	 */
  	if (unlikely(netpoll_tx_running(dev)))
  		return dsa_slave_netpoll_send_skb(dev, skb);
  
  	/* Queue the SKB for transmission on the parent interface, but
  	 * do not modify its EtherType
  	 */
  	skb->dev = dsa_slave_to_master(dev);
  	dev_queue_xmit(skb);
  
  	return NETDEV_TX_OK;
  }
  EXPORT_SYMBOL_GPL(dsa_enqueue_skb);
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
471
472
473
  static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
474
  	struct pcpu_sw_netstats *s;
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
475
  	struct sk_buff *nskb;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
476

5f6b4e14c   Florian Fainelli   net: dsa: User pe...
477
478
479
480
481
  	s = this_cpu_ptr(p->stats64);
  	u64_stats_update_begin(&s->syncp);
  	s->tx_packets++;
  	s->tx_bytes += skb->len;
  	u64_stats_update_end(&s->syncp);
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
482

876713751   Vladimir Oltean   net: dsa: Initial...
483
  	DSA_SKB_CB(skb)->deferred_xmit = false;
146d442c2   Vladimir Oltean   net: dsa: Keep a ...
484
  	DSA_SKB_CB(skb)->clone = NULL;
876713751   Vladimir Oltean   net: dsa: Initial...
485

90af1059c   Brandon Streiff   net: dsa: forward...
486
487
488
489
  	/* Identify PTP protocol packets, clone them, and pass them to the
  	 * switch driver
  	 */
  	dsa_skb_tx_timestamp(p, skb);
fe47d5630   Vivien Didelot   net: dsa: factor ...
490
491
492
  	/* Transmit function may have to reallocate the original SKB,
  	 * in which case it must have freed it. Only free it here on error.
  	 */
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
493
  	nskb = p->xmit(skb, dev);
fe47d5630   Vivien Didelot   net: dsa: factor ...
494
  	if (!nskb) {
97a69a0de   Vladimir Oltean   net: dsa: Add sup...
495
496
  		if (!DSA_SKB_CB(skb)->deferred_xmit)
  			kfree_skb(skb);
4ed70ce9f   Florian Fainelli   net: dsa: Refacto...
497
  		return NETDEV_TX_OK;
fe47d5630   Vivien Didelot   net: dsa: factor ...
498
  	}
5aed85cec   Florian Fainelli   net: dsa: allow s...
499

97a69a0de   Vladimir Oltean   net: dsa: Add sup...
500
501
  	return dsa_enqueue_skb(nskb, dev);
  }
04ff53f96   Florian Fainelli   net: dsa: Add net...
502

97a69a0de   Vladimir Oltean   net: dsa: Add sup...
503
504
505
  void *dsa_defer_xmit(struct sk_buff *skb, struct net_device *dev)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
5aed85cec   Florian Fainelli   net: dsa: allow s...
506

97a69a0de   Vladimir Oltean   net: dsa: Add sup...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  	DSA_SKB_CB(skb)->deferred_xmit = true;
  
  	skb_queue_tail(&dp->xmit_queue, skb);
  	schedule_work(&dp->xmit_work);
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(dsa_defer_xmit);
  
  static void dsa_port_xmit_work(struct work_struct *work)
  {
  	struct dsa_port *dp = container_of(work, struct dsa_port, xmit_work);
  	struct dsa_switch *ds = dp->ds;
  	struct sk_buff *skb;
  
  	if (unlikely(!ds->ops->port_deferred_xmit))
  		return;
  
  	while ((skb = skb_dequeue(&dp->xmit_queue)) != NULL)
  		ds->ops->port_deferred_xmit(ds, dp->index, skb);
5aed85cec   Florian Fainelli   net: dsa: allow s...
526
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
527
  /* ethtool operations *******************************************************/
91da11f87   Lennert Buytenhek   net: Distributed ...
528

91da11f87   Lennert Buytenhek   net: Distributed ...
529
530
531
  static void dsa_slave_get_drvinfo(struct net_device *dev,
  				  struct ethtool_drvinfo *drvinfo)
  {
7826d43f2   Jiri Pirko   ethtool: fix drvi...
532
  	strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver));
7826d43f2   Jiri Pirko   ethtool: fix drvi...
533
534
  	strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version));
  	strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
91da11f87   Lennert Buytenhek   net: Distributed ...
535
  }
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
536
537
  static int dsa_slave_get_regs_len(struct net_device *dev)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
538
539
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
540

9d490b4ee   Vivien Didelot   net: dsa: rename ...
541
  	if (ds->ops->get_regs_len)
d945097bb   Vivien Didelot   net: dsa: add sla...
542
  		return ds->ops->get_regs_len(ds, dp->index);
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
543
544
545
546
547
548
549
  
  	return -EOPNOTSUPP;
  }
  
  static void
  dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
550
551
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
552

9d490b4ee   Vivien Didelot   net: dsa: rename ...
553
  	if (ds->ops->get_regs)
d945097bb   Vivien Didelot   net: dsa: add sla...
554
  		ds->ops->get_regs(ds, dp->index, regs, _p);
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
555
  }
aab9c4067   Florian Fainelli   net: dsa: Plug in...
556
557
558
559
560
561
  static int dsa_slave_nway_reset(struct net_device *dev)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	return phylink_ethtool_nway_reset(dp->pl);
  }
6793abb4e   Guenter Roeck   net: dsa: Add sup...
562
563
  static int dsa_slave_get_eeprom_len(struct net_device *dev)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
564
565
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
566

0e5760440   Andrew Lunn   net: dsa: slave: ...
567
  	if (ds->cd && ds->cd->eeprom_len)
ff04955c2   Andrew Lunn   dsa: Rename switc...
568
  		return ds->cd->eeprom_len;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
569

9d490b4ee   Vivien Didelot   net: dsa: rename ...
570
571
  	if (ds->ops->get_eeprom_len)
  		return ds->ops->get_eeprom_len(ds);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
572
573
574
575
576
577
578
  
  	return 0;
  }
  
  static int dsa_slave_get_eeprom(struct net_device *dev,
  				struct ethtool_eeprom *eeprom, u8 *data)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
579
580
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
581

9d490b4ee   Vivien Didelot   net: dsa: rename ...
582
583
  	if (ds->ops->get_eeprom)
  		return ds->ops->get_eeprom(ds, eeprom, data);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
584
585
586
587
588
589
590
  
  	return -EOPNOTSUPP;
  }
  
  static int dsa_slave_set_eeprom(struct net_device *dev,
  				struct ethtool_eeprom *eeprom, u8 *data)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
591
592
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
6793abb4e   Guenter Roeck   net: dsa: Add sup...
593

9d490b4ee   Vivien Didelot   net: dsa: rename ...
594
595
  	if (ds->ops->set_eeprom)
  		return ds->ops->set_eeprom(ds, eeprom, data);
6793abb4e   Guenter Roeck   net: dsa: Add sup...
596
597
598
  
  	return -EOPNOTSUPP;
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
599
600
601
  static void dsa_slave_get_strings(struct net_device *dev,
  				  uint32_t stringset, uint8_t *data)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
602
603
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
91da11f87   Lennert Buytenhek   net: Distributed ...
604
605
606
607
608
609
610
611
  
  	if (stringset == ETH_SS_STATS) {
  		int len = ETH_GSTRING_LEN;
  
  		strncpy(data, "tx_packets", len);
  		strncpy(data + len, "tx_bytes", len);
  		strncpy(data + 2 * len, "rx_packets", len);
  		strncpy(data + 3 * len, "rx_bytes", len);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
612
  		if (ds->ops->get_strings)
89f090483   Florian Fainelli   net: dsa: Pass st...
613
614
  			ds->ops->get_strings(ds, dp->index, stringset,
  					     data + 4 * len);
91da11f87   Lennert Buytenhek   net: Distributed ...
615
616
617
618
619
620
621
  	}
  }
  
  static void dsa_slave_get_ethtool_stats(struct net_device *dev,
  					struct ethtool_stats *stats,
  					uint64_t *data)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
622
  	struct dsa_port *dp = dsa_slave_to_port(dev);
91da11f87   Lennert Buytenhek   net: Distributed ...
623
  	struct dsa_slave_priv *p = netdev_priv(dev);
d945097bb   Vivien Didelot   net: dsa: add sla...
624
  	struct dsa_switch *ds = dp->ds;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
625
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
626
  	unsigned int start;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
  	int i;
  
  	for_each_possible_cpu(i) {
  		u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
  
  		s = per_cpu_ptr(p->stats64, i);
  		do {
  			start = u64_stats_fetch_begin_irq(&s->syncp);
  			tx_packets = s->tx_packets;
  			tx_bytes = s->tx_bytes;
  			rx_packets = s->rx_packets;
  			rx_bytes = s->rx_bytes;
  		} while (u64_stats_fetch_retry_irq(&s->syncp, start));
  		data[0] += tx_packets;
  		data[1] += tx_bytes;
  		data[2] += rx_packets;
  		data[3] += rx_bytes;
  	}
9d490b4ee   Vivien Didelot   net: dsa: rename ...
645
  	if (ds->ops->get_ethtool_stats)
d945097bb   Vivien Didelot   net: dsa: add sla...
646
  		ds->ops->get_ethtool_stats(ds, dp->index, data + 4);
91da11f87   Lennert Buytenhek   net: Distributed ...
647
648
649
650
  }
  
  static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
651
652
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
91da11f87   Lennert Buytenhek   net: Distributed ...
653
654
655
656
657
  
  	if (sset == ETH_SS_STATS) {
  		int count;
  
  		count = 4;
9d490b4ee   Vivien Didelot   net: dsa: rename ...
658
  		if (ds->ops->get_sset_count)
89f090483   Florian Fainelli   net: dsa: Pass st...
659
  			count += ds->ops->get_sset_count(ds, dp->index, sset);
91da11f87   Lennert Buytenhek   net: Distributed ...
660
661
662
663
664
665
  
  		return count;
  	}
  
  	return -EOPNOTSUPP;
  }
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
666
667
  static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
668
669
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
670

aab9c4067   Florian Fainelli   net: dsa: Plug in...
671
  	phylink_ethtool_get_wol(dp->pl, w);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
672
  	if (ds->ops->get_wol)
d945097bb   Vivien Didelot   net: dsa: add sla...
673
  		ds->ops->get_wol(ds, dp->index, w);
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
674
675
676
677
  }
  
  static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
678
679
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
680
  	int ret = -EOPNOTSUPP;
aab9c4067   Florian Fainelli   net: dsa: Plug in...
681
  	phylink_ethtool_set_wol(dp->pl, w);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
682
  	if (ds->ops->set_wol)
d945097bb   Vivien Didelot   net: dsa: add sla...
683
  		ret = ds->ops->set_wol(ds, dp->index, w);
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
684
685
686
  
  	return ret;
  }
7905288f0   Florian Fainelli   net: dsa: allow s...
687
688
  static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
689
690
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
7905288f0   Florian Fainelli   net: dsa: allow s...
691
  	int ret;
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
692
  	/* Port's PHY and MAC both need to be EEE capable */
00670cb8a   Dan Carpenter   net: dsa: Fix NUL...
693
  	if (!dev->phydev || !dp->pl)
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
694
  		return -ENODEV;
08f500610   Vivien Didelot   net: dsa: rename ...
695
  	if (!ds->ops->set_mac_eee)
7905288f0   Florian Fainelli   net: dsa: allow s...
696
  		return -EOPNOTSUPP;
d945097bb   Vivien Didelot   net: dsa: add sla...
697
  	ret = ds->ops->set_mac_eee(ds, dp->index, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
698
699
  	if (ret)
  		return ret;
aab9c4067   Florian Fainelli   net: dsa: Plug in...
700
  	return phylink_ethtool_set_eee(dp->pl, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
701
702
703
704
  }
  
  static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
705
706
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
7905288f0   Florian Fainelli   net: dsa: allow s...
707
  	int ret;
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
708
  	/* Port's PHY and MAC both need to be EEE capable */
00670cb8a   Dan Carpenter   net: dsa: Fix NUL...
709
  	if (!dev->phydev || !dp->pl)
7b9cc7384   Vivien Didelot   net: dsa: PHY dev...
710
  		return -ENODEV;
08f500610   Vivien Didelot   net: dsa: rename ...
711
  	if (!ds->ops->get_mac_eee)
7905288f0   Florian Fainelli   net: dsa: allow s...
712
  		return -EOPNOTSUPP;
d945097bb   Vivien Didelot   net: dsa: add sla...
713
  	ret = ds->ops->get_mac_eee(ds, dp->index, e);
7905288f0   Florian Fainelli   net: dsa: allow s...
714
715
  	if (ret)
  		return ret;
aab9c4067   Florian Fainelli   net: dsa: Plug in...
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
  	return phylink_ethtool_get_eee(dp->pl, e);
  }
  
  static int dsa_slave_get_link_ksettings(struct net_device *dev,
  					struct ethtool_link_ksettings *cmd)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	return phylink_ethtool_ksettings_get(dp->pl, cmd);
  }
  
  static int dsa_slave_set_link_ksettings(struct net_device *dev,
  					const struct ethtool_link_ksettings *cmd)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	return phylink_ethtool_ksettings_set(dp->pl, cmd);
7905288f0   Florian Fainelli   net: dsa: allow s...
733
  }
04ff53f96   Florian Fainelli   net: dsa: Add net...
734
735
736
737
  #ifdef CONFIG_NET_POLL_CONTROLLER
  static int dsa_slave_netpoll_setup(struct net_device *dev,
  				   struct netpoll_info *ni)
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
738
  	struct net_device *master = dsa_slave_to_master(dev);
04ff53f96   Florian Fainelli   net: dsa: Add net...
739
  	struct dsa_slave_priv *p = netdev_priv(dev);
04ff53f96   Florian Fainelli   net: dsa: Add net...
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
  	struct netpoll *netpoll;
  	int err = 0;
  
  	netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
  	if (!netpoll)
  		return -ENOMEM;
  
  	err = __netpoll_setup(netpoll, master);
  	if (err) {
  		kfree(netpoll);
  		goto out;
  	}
  
  	p->netpoll = netpoll;
  out:
  	return err;
  }
  
  static void dsa_slave_netpoll_cleanup(struct net_device *dev)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct netpoll *netpoll = p->netpoll;
  
  	if (!netpoll)
  		return;
  
  	p->netpoll = NULL;
c9fbd71f7   Debabrata Banerjee   netpoll: allow cl...
767
  	__netpoll_free(netpoll);
04ff53f96   Florian Fainelli   net: dsa: Add net...
768
769
770
771
772
773
  }
  
  static void dsa_slave_poll_controller(struct net_device *dev)
  {
  }
  #endif
44bb765cf   Florian Fainelli   net: dsa: Impleme...
774
775
776
  static int dsa_slave_get_phys_port_name(struct net_device *dev,
  					char *name, size_t len)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
777
  	struct dsa_port *dp = dsa_slave_to_port(dev);
44bb765cf   Florian Fainelli   net: dsa: Impleme...
778

d484210bf   Jiri Pirko   dsa: do not suppo...
779
780
781
782
783
784
  	/* For non-legacy ports, devlink is used and it takes
  	 * care of the name generation. This ndo implementation
  	 * should be removed with legacy support.
  	 */
  	if (dp->ds->devlink)
  		return -EOPNOTSUPP;
d945097bb   Vivien Didelot   net: dsa: add sla...
785
  	if (snprintf(name, len, "p%d", dp->index) >= len)
44bb765cf   Florian Fainelli   net: dsa: Impleme...
786
  		return -EINVAL;
3a543ef47   Florian Fainelli   net: dsa: Impleme...
787
788
789
  
  	return 0;
  }
f50f21274   Florian Fainelli   net: dsa: Add plu...
790
  static struct dsa_mall_tc_entry *
4fa7b7188   Vivien Didelot   net: dsa: better ...
791
  dsa_slave_mall_tc_entry_find(struct net_device *dev, unsigned long cookie)
f50f21274   Florian Fainelli   net: dsa: Add plu...
792
  {
4fa7b7188   Vivien Didelot   net: dsa: better ...
793
  	struct dsa_slave_priv *p = netdev_priv(dev);
f50f21274   Florian Fainelli   net: dsa: Add plu...
794
795
796
797
798
799
800
801
802
803
  	struct dsa_mall_tc_entry *mall_tc_entry;
  
  	list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list)
  		if (mall_tc_entry->cookie == cookie)
  			return mall_tc_entry;
  
  	return NULL;
  }
  
  static int dsa_slave_add_cls_matchall(struct net_device *dev,
f50f21274   Florian Fainelli   net: dsa: Add plu...
804
805
806
  				      struct tc_cls_matchall_offload *cls,
  				      bool ingress)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
807
  	struct dsa_port *dp = dsa_slave_to_port(dev);
f50f21274   Florian Fainelli   net: dsa: Add plu...
808
809
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_mall_tc_entry *mall_tc_entry;
5fd9fc4e2   Jiri Pirko   net: sched: push ...
810
  	__be16 protocol = cls->common.protocol;
d945097bb   Vivien Didelot   net: dsa: add sla...
811
  	struct dsa_switch *ds = dp->ds;
9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
812
  	struct flow_action_entry *act;
d945097bb   Vivien Didelot   net: dsa: add sla...
813
  	struct dsa_port *to_dp;
f50f21274   Florian Fainelli   net: dsa: Add plu...
814
  	int err = -EOPNOTSUPP;
f50f21274   Florian Fainelli   net: dsa: Add plu...
815
816
817
  
  	if (!ds->ops->port_mirror_add)
  		return err;
9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
818
  	if (!flow_offload_has_one_action(&cls->rule->action))
f50f21274   Florian Fainelli   net: dsa: Add plu...
819
  		return err;
9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
820
  	act = &cls->rule->action.entries[0];
f50f21274   Florian Fainelli   net: dsa: Add plu...
821

9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
822
  	if (act->id == FLOW_ACTION_MIRRED && protocol == htons(ETH_P_ALL)) {
f50f21274   Florian Fainelli   net: dsa: Add plu...
823
  		struct dsa_mall_mirror_tc_entry *mirror;
9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
824
  		if (!act->dev)
f50f21274   Florian Fainelli   net: dsa: Add plu...
825
  			return -EINVAL;
9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
826
  		if (!dsa_slave_dev_check(act->dev))
f50f21274   Florian Fainelli   net: dsa: Add plu...
827
828
829
830
831
832
833
834
835
  			return -EOPNOTSUPP;
  
  		mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
  		if (!mall_tc_entry)
  			return -ENOMEM;
  
  		mall_tc_entry->cookie = cls->cookie;
  		mall_tc_entry->type = DSA_PORT_MALL_MIRROR;
  		mirror = &mall_tc_entry->mirror;
9681e8b3e   Pieter Jansen van Vuuren   net/dsa: use inte...
836
  		to_dp = dsa_slave_to_port(act->dev);
f50f21274   Florian Fainelli   net: dsa: Add plu...
837

d945097bb   Vivien Didelot   net: dsa: add sla...
838
  		mirror->to_local_port = to_dp->index;
f50f21274   Florian Fainelli   net: dsa: Add plu...
839
  		mirror->ingress = ingress;
d945097bb   Vivien Didelot   net: dsa: add sla...
840
  		err = ds->ops->port_mirror_add(ds, dp->index, mirror, ingress);
f50f21274   Florian Fainelli   net: dsa: Add plu...
841
842
843
844
845
846
847
848
849
850
851
852
853
854
  		if (err) {
  			kfree(mall_tc_entry);
  			return err;
  		}
  
  		list_add_tail(&mall_tc_entry->list, &p->mall_tc_list);
  	}
  
  	return 0;
  }
  
  static void dsa_slave_del_cls_matchall(struct net_device *dev,
  				       struct tc_cls_matchall_offload *cls)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
855
  	struct dsa_port *dp = dsa_slave_to_port(dev);
f50f21274   Florian Fainelli   net: dsa: Add plu...
856
  	struct dsa_mall_tc_entry *mall_tc_entry;
d945097bb   Vivien Didelot   net: dsa: add sla...
857
  	struct dsa_switch *ds = dp->ds;
f50f21274   Florian Fainelli   net: dsa: Add plu...
858
859
860
  
  	if (!ds->ops->port_mirror_del)
  		return;
4fa7b7188   Vivien Didelot   net: dsa: better ...
861
  	mall_tc_entry = dsa_slave_mall_tc_entry_find(dev, cls->cookie);
f50f21274   Florian Fainelli   net: dsa: Add plu...
862
863
864
865
866
867
868
  	if (!mall_tc_entry)
  		return;
  
  	list_del(&mall_tc_entry->list);
  
  	switch (mall_tc_entry->type) {
  	case DSA_PORT_MALL_MIRROR:
d945097bb   Vivien Didelot   net: dsa: add sla...
869
  		ds->ops->port_mirror_del(ds, dp->index, &mall_tc_entry->mirror);
f50f21274   Florian Fainelli   net: dsa: Add plu...
870
871
872
873
874
875
876
  		break;
  	default:
  		WARN_ON(1);
  	}
  
  	kfree(mall_tc_entry);
  }
3fbae382f   Jiri Pirko   dsa: push cls_mat...
877
  static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev,
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
878
879
  					   struct tc_cls_matchall_offload *cls,
  					   bool ingress)
f50f21274   Florian Fainelli   net: dsa: Add plu...
880
  {
5fd9fc4e2   Jiri Pirko   net: sched: push ...
881
  	if (cls->common.chain_index)
a5fcf8a6c   Jiri Pirko   net: propagate tc...
882
  		return -EOPNOTSUPP;
f50f21274   Florian Fainelli   net: dsa: Add plu...
883

3fbae382f   Jiri Pirko   dsa: push cls_mat...
884
885
  	switch (cls->command) {
  	case TC_CLSMATCHALL_REPLACE:
5fd9fc4e2   Jiri Pirko   net: sched: push ...
886
  		return dsa_slave_add_cls_matchall(dev, cls, ingress);
3fbae382f   Jiri Pirko   dsa: push cls_mat...
887
888
889
890
891
892
893
  	case TC_CLSMATCHALL_DESTROY:
  		dsa_slave_del_cls_matchall(dev, cls);
  		return 0;
  	default:
  		return -EOPNOTSUPP;
  	}
  }
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
894
895
896
897
  static int dsa_slave_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
  				       void *cb_priv, bool ingress)
  {
  	struct net_device *dev = cb_priv;
44ae12a76   Jiri Pirko   net: sched: move ...
898
899
  	if (!tc_can_offload(dev))
  		return -EOPNOTSUPP;
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
  	switch (type) {
  	case TC_SETUP_CLSMATCHALL:
  		return dsa_slave_setup_tc_cls_matchall(dev, type_data, ingress);
  	default:
  		return -EOPNOTSUPP;
  	}
  }
  
  static int dsa_slave_setup_tc_block_cb_ig(enum tc_setup_type type,
  					  void *type_data, void *cb_priv)
  {
  	return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, true);
  }
  
  static int dsa_slave_setup_tc_block_cb_eg(enum tc_setup_type type,
  					  void *type_data, void *cb_priv)
  {
  	return dsa_slave_setup_tc_block_cb(type, type_data, cb_priv, false);
  }
955bcb6ea   Pablo Neira Ayuso   drivers: net: use...
919
  static LIST_HEAD(dsa_slave_block_cb_list);
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
920
  static int dsa_slave_setup_tc_block(struct net_device *dev,
955bcb6ea   Pablo Neira Ayuso   drivers: net: use...
921
  				    struct flow_block_offload *f)
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
922
  {
955bcb6ea   Pablo Neira Ayuso   drivers: net: use...
923
  	struct flow_block_cb *block_cb;
a73233115   Pablo Neira Ayuso   net: flow_offload...
924
  	flow_setup_cb_t *cb;
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
925

32f8c4093   Pablo Neira Ayuso   net: flow_offload...
926
  	if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
927
  		cb = dsa_slave_setup_tc_block_cb_ig;
32f8c4093   Pablo Neira Ayuso   net: flow_offload...
928
  	else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
929
930
931
  		cb = dsa_slave_setup_tc_block_cb_eg;
  	else
  		return -EOPNOTSUPP;
955bcb6ea   Pablo Neira Ayuso   drivers: net: use...
932
  	f->driver_block_list = &dsa_slave_block_cb_list;
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
933
  	switch (f->command) {
9c0e189ec   Pablo Neira Ayuso   net: flow_offload...
934
  	case FLOW_BLOCK_BIND:
0d4fd02e7   Pablo Neira Ayuso   net: flow_offload...
935
936
  		if (flow_block_cb_is_busy(cb, dev, &dsa_slave_block_cb_list))
  			return -EBUSY;
0c7294dda   Pablo Neira Ayuso   net: flow_offload...
937
  		block_cb = flow_block_cb_alloc(cb, dev, dev, NULL);
955bcb6ea   Pablo Neira Ayuso   drivers: net: use...
938
939
940
941
942
943
  		if (IS_ERR(block_cb))
  			return PTR_ERR(block_cb);
  
  		flow_block_cb_add(block_cb, f);
  		list_add_tail(&block_cb->driver_list, &dsa_slave_block_cb_list);
  		return 0;
9c0e189ec   Pablo Neira Ayuso   net: flow_offload...
944
  	case FLOW_BLOCK_UNBIND:
14bfb13f0   Pablo Neira Ayuso   net: flow_offload...
945
  		block_cb = flow_block_cb_lookup(f->block, cb, dev);
955bcb6ea   Pablo Neira Ayuso   drivers: net: use...
946
947
948
949
950
  		if (!block_cb)
  			return -ENOENT;
  
  		flow_block_cb_remove(block_cb, f);
  		list_del(&block_cb->driver_list);
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
951
952
953
954
955
  		return 0;
  	default:
  		return -EOPNOTSUPP;
  	}
  }
3fbae382f   Jiri Pirko   dsa: push cls_mat...
956
  static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type,
de4784ca0   Jiri Pirko   net: sched: get r...
957
  			      void *type_data)
3fbae382f   Jiri Pirko   dsa: push cls_mat...
958
  {
47d23af29   Vladimir Oltean   net: dsa: Pass nd...
959
960
961
962
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
  
  	if (type == TC_SETUP_BLOCK)
6b3eb752b   Jiri Pirko   dsa: Convert ndo_...
963
  		return dsa_slave_setup_tc_block(dev, type_data);
47d23af29   Vladimir Oltean   net: dsa: Pass nd...
964
965
  
  	if (!ds->ops->port_setup_tc)
a5fcf8a6c   Jiri Pirko   net: propagate tc...
966
  		return -EOPNOTSUPP;
47d23af29   Vladimir Oltean   net: dsa: Pass nd...
967
968
  
  	return ds->ops->port_setup_tc(ds, dp->index, type, type_data);
f50f21274   Florian Fainelli   net: dsa: Add plu...
969
  }
f613ed665   Florian Fainelli   net: dsa: Add sup...
970
971
972
973
  static void dsa_slave_get_stats64(struct net_device *dev,
  				  struct rtnl_link_stats64 *stats)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
974
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
975
  	unsigned int start;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
976
  	int i;
f613ed665   Florian Fainelli   net: dsa: Add sup...
977
978
  
  	netdev_stats_to_stats64(stats, &dev->stats);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
  	for_each_possible_cpu(i) {
  		u64 tx_packets, tx_bytes, rx_packets, rx_bytes;
  
  		s = per_cpu_ptr(p->stats64, i);
  		do {
  			start = u64_stats_fetch_begin_irq(&s->syncp);
  			tx_packets = s->tx_packets;
  			tx_bytes = s->tx_bytes;
  			rx_packets = s->rx_packets;
  			rx_bytes = s->rx_bytes;
  		} while (u64_stats_fetch_retry_irq(&s->syncp, start));
  
  		stats->tx_packets += tx_packets;
  		stats->tx_bytes += tx_bytes;
  		stats->rx_packets += rx_packets;
  		stats->rx_bytes += rx_bytes;
  	}
f613ed665   Florian Fainelli   net: dsa: Add sup...
996
  }
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
997
998
999
  static int dsa_slave_get_rxnfc(struct net_device *dev,
  			       struct ethtool_rxnfc *nfc, u32 *rule_locs)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
1000
1001
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
1002
1003
1004
  
  	if (!ds->ops->get_rxnfc)
  		return -EOPNOTSUPP;
d945097bb   Vivien Didelot   net: dsa: add sla...
1005
  	return ds->ops->get_rxnfc(ds, dp->index, nfc, rule_locs);
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
1006
1007
1008
1009
1010
  }
  
  static int dsa_slave_set_rxnfc(struct net_device *dev,
  			       struct ethtool_rxnfc *nfc)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
1011
1012
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
1013
1014
1015
  
  	if (!ds->ops->set_rxnfc)
  		return -EOPNOTSUPP;
d945097bb   Vivien Didelot   net: dsa: add sla...
1016
  	return ds->ops->set_rxnfc(ds, dp->index, nfc);
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
1017
  }
0336369d3   Brandon Streiff   net: dsa: forward...
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
  static int dsa_slave_get_ts_info(struct net_device *dev,
  				 struct ethtool_ts_info *ts)
  {
  	struct dsa_slave_priv *p = netdev_priv(dev);
  	struct dsa_switch *ds = p->dp->ds;
  
  	if (!ds->ops->get_ts_info)
  		return -EOPNOTSUPP;
  
  	return ds->ops->get_ts_info(ds, p->dp->index, ts);
  }
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1029
1030
1031
1032
  static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
  				     u16 vid)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1033
1034
1035
1036
1037
1038
1039
  	struct bridge_vlan_info info;
  	int ret;
  
  	/* Check for a possible bridge VLAN entry now since there is no
  	 * need to emulate the switchdev prepare + commit phase.
  	 */
  	if (dp->bridge_dev) {
c5335d737   Vivien Didelot   net: dsa: check b...
1040
1041
  		if (!br_vlan_enabled(dp->bridge_dev))
  			return 0;
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1042
1043
1044
1045
1046
1047
1048
1049
  		/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
  		 * device, respectively the VID is not found, returning
  		 * 0 means success, which is a failure for us here.
  		 */
  		ret = br_vlan_get_info(dp->bridge_dev, vid, &info);
  		if (ret == 0)
  			return -EBUSY;
  	}
cf360866b   Vivien Didelot   net: dsa: do not ...
1050
  	ret = dsa_port_vid_add(dp, vid, 0);
9b236d2a6   Vladimir Oltean   net: dsa: Adverti...
1051
  	if (ret)
cf360866b   Vivien Didelot   net: dsa: do not ...
1052
  		return ret;
7e1741b47   Vivien Didelot   net: dsa: program...
1053
  	ret = dsa_port_vid_add(dp->cpu_dp, vid, 0);
9b236d2a6   Vladimir Oltean   net: dsa: Adverti...
1054
  	if (ret)
7e1741b47   Vivien Didelot   net: dsa: program...
1055
  		return ret;
cf360866b   Vivien Didelot   net: dsa: do not ...
1056
  	return 0;
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1057
1058
1059
1060
1061
1062
  }
  
  static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
  				      u16 vid)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1063
1064
1065
1066
1067
1068
1069
  	struct bridge_vlan_info info;
  	int ret;
  
  	/* Check for a possible bridge VLAN entry now since there is no
  	 * need to emulate the switchdev prepare + commit phase.
  	 */
  	if (dp->bridge_dev) {
c5335d737   Vivien Didelot   net: dsa: check b...
1070
1071
  		if (!br_vlan_enabled(dp->bridge_dev))
  			return 0;
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1072
1073
1074
1075
1076
1077
1078
1079
  		/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
  		 * device, respectively the VID is not found, returning
  		 * 0 means success, which is a failure for us here.
  		 */
  		ret = br_vlan_get_info(dp->bridge_dev, vid, &info);
  		if (ret == 0)
  			return -EBUSY;
  	}
7e1741b47   Vivien Didelot   net: dsa: program...
1080
1081
1082
  	/* Do not deprogram the CPU port as it may be shared with other user
  	 * ports which can be members of this VLAN as well.
  	 */
9b236d2a6   Vladimir Oltean   net: dsa: Adverti...
1083
  	return dsa_port_vid_del(dp, vid);
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1084
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
1085
  static const struct ethtool_ops dsa_slave_ethtool_ops = {
91da11f87   Lennert Buytenhek   net: Distributed ...
1086
  	.get_drvinfo		= dsa_slave_get_drvinfo,
3d762a0f0   Guenter Roeck   net: dsa: Add sup...
1087
1088
  	.get_regs_len		= dsa_slave_get_regs_len,
  	.get_regs		= dsa_slave_get_regs,
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1089
  	.nway_reset		= dsa_slave_nway_reset,
c4aef9fc0   Florian Fainelli   net: dsa: Elimina...
1090
  	.get_link		= ethtool_op_get_link,
6793abb4e   Guenter Roeck   net: dsa: Add sup...
1091
1092
1093
  	.get_eeprom_len		= dsa_slave_get_eeprom_len,
  	.get_eeprom		= dsa_slave_get_eeprom,
  	.set_eeprom		= dsa_slave_set_eeprom,
91da11f87   Lennert Buytenhek   net: Distributed ...
1094
1095
1096
  	.get_strings		= dsa_slave_get_strings,
  	.get_ethtool_stats	= dsa_slave_get_ethtool_stats,
  	.get_sset_count		= dsa_slave_get_sset_count,
19e57c4e6   Florian Fainelli   net: dsa: add {ge...
1097
1098
  	.set_wol		= dsa_slave_set_wol,
  	.get_wol		= dsa_slave_get_wol,
7905288f0   Florian Fainelli   net: dsa: allow s...
1099
1100
  	.set_eee		= dsa_slave_set_eee,
  	.get_eee		= dsa_slave_get_eee,
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1101
1102
  	.get_link_ksettings	= dsa_slave_get_link_ksettings,
  	.set_link_ksettings	= dsa_slave_set_link_ksettings,
bf9f26485   Florian Fainelli   net: dsa: Hook {g...
1103
1104
  	.get_rxnfc		= dsa_slave_get_rxnfc,
  	.set_rxnfc		= dsa_slave_set_rxnfc,
0336369d3   Brandon Streiff   net: dsa: forward...
1105
  	.get_ts_info		= dsa_slave_get_ts_info,
91da11f87   Lennert Buytenhek   net: Distributed ...
1106
  };
2a93c1a36   Florian Fainelli   net: dsa: Allow c...
1107
1108
1109
1110
  /* legacy way, bypassing the bridge *****************************************/
  int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
  		       struct net_device *dev,
  		       const unsigned char *addr, u16 vid,
87b0984eb   Petr Machata   net: Add extack a...
1111
1112
  		       u16 flags,
  		       struct netlink_ext_ack *extack)
2a93c1a36   Florian Fainelli   net: dsa: Allow c...
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	return dsa_port_fdb_add(dp, addr, vid);
  }
  
  int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
  		       struct net_device *dev,
  		       const unsigned char *addr, u16 vid)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	return dsa_port_fdb_del(dp, addr, vid);
  }
716efee20   Jiri Pirko   dsa: implement nd...
1127
1128
1129
1130
1131
1132
  static struct devlink_port *dsa_slave_get_devlink_port(struct net_device *dev)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  
  	return dp->ds->devlink ? &dp->devlink_port : NULL;
  }
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
1133
  static const struct net_device_ops dsa_slave_netdev_ops = {
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1134
1135
  	.ndo_open	 	= dsa_slave_open,
  	.ndo_stop		= dsa_slave_close,
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
1136
  	.ndo_start_xmit		= dsa_slave_xmit,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1137
1138
  	.ndo_change_rx_flags	= dsa_slave_change_rx_flags,
  	.ndo_set_rx_mode	= dsa_slave_set_rx_mode,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1139
  	.ndo_set_mac_address	= dsa_slave_set_mac_address,
37b8da1a3   Arkadi Sharshevsky   net: dsa: Move FD...
1140
1141
  	.ndo_fdb_add		= dsa_legacy_fdb_add,
  	.ndo_fdb_del		= dsa_legacy_fdb_del,
2bedde1ab   Arkadi Sharshevsky   net: dsa: Move FD...
1142
  	.ndo_fdb_dump		= dsa_slave_fdb_dump,
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1143
  	.ndo_do_ioctl		= dsa_slave_ioctl,
abd2be00d   Nicolas Dichtel   dsa: implement nd...
1144
  	.ndo_get_iflink		= dsa_slave_get_iflink,
04ff53f96   Florian Fainelli   net: dsa: Add net...
1145
1146
1147
1148
1149
  #ifdef CONFIG_NET_POLL_CONTROLLER
  	.ndo_netpoll_setup	= dsa_slave_netpoll_setup,
  	.ndo_netpoll_cleanup	= dsa_slave_netpoll_cleanup,
  	.ndo_poll_controller	= dsa_slave_poll_controller,
  #endif
44bb765cf   Florian Fainelli   net: dsa: Impleme...
1150
  	.ndo_get_phys_port_name	= dsa_slave_get_phys_port_name,
f50f21274   Florian Fainelli   net: dsa: Add plu...
1151
  	.ndo_setup_tc		= dsa_slave_setup_tc,
f613ed665   Florian Fainelli   net: dsa: Add sup...
1152
  	.ndo_get_stats64	= dsa_slave_get_stats64,
929d6c145   Florian Fainelli   net: dsa: Impleme...
1153
  	.ndo_get_port_parent_id	= dsa_slave_get_port_parent_id,
061f6a505   Florian Fainelli   net: dsa: Add ndo...
1154
1155
  	.ndo_vlan_rx_add_vid	= dsa_slave_vlan_rx_add_vid,
  	.ndo_vlan_rx_kill_vid	= dsa_slave_vlan_rx_kill_vid,
716efee20   Jiri Pirko   dsa: implement nd...
1156
  	.ndo_get_devlink_port	= dsa_slave_get_devlink_port,
98237d433   Scott Feldman   switchdev: use ne...
1157
  };
f37db85d0   Florian Fainelli   net: dsa: Set a "...
1158
1159
1160
  static struct device_type dsa_type = {
  	.name	= "dsa",
  };
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
  void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
  {
  	const struct dsa_port *dp = dsa_to_port(ds, port);
  
  	phylink_mac_change(dp->pl, up);
  }
  EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
  
  static void dsa_slave_phylink_fixed_state(struct net_device *dev,
  					  struct phylink_link_state *state)
  {
  	struct dsa_port *dp = dsa_slave_to_port(dev);
  	struct dsa_switch *ds = dp->ds;
  
  	/* No need to check that this operation is valid, the callback would
  	 * not be called if it was not.
  	 */
  	ds->ops->phylink_fixed_state(ds, dp->index, state);
ce31b31c6   Florian Fainelli   net: dsa: allow u...
1179
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
1180
  /* slave device setup *******************************************************/
4fa7b7188   Vivien Didelot   net: dsa: better ...
1181
  static int dsa_slave_phy_connect(struct net_device *slave_dev, int addr)
c305c1651   Florian Fainelli   net: dsa: move PH...
1182
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
1183
  	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
d945097bb   Vivien Didelot   net: dsa: add sla...
1184
  	struct dsa_switch *ds = dp->ds;
c305c1651   Florian Fainelli   net: dsa: move PH...
1185

0115dcd17   Vivien Didelot   net: dsa: use sla...
1186
1187
  	slave_dev->phydev = mdiobus_get_phy(ds->slave_mii_bus, addr);
  	if (!slave_dev->phydev) {
d25b8e742   Russell King   net: dsa: better ...
1188
1189
  		netdev_err(slave_dev, "no phy at %d
  ", addr);
c305c1651   Florian Fainelli   net: dsa: move PH...
1190
  		return -ENODEV;
d25b8e742   Russell King   net: dsa: better ...
1191
  	}
c305c1651   Florian Fainelli   net: dsa: move PH...
1192

aab9c4067   Florian Fainelli   net: dsa: Plug in...
1193
  	return phylink_connect_phy(dp->pl, slave_dev->phydev);
11d8f3dda   Florian Fainelli   net: dsa: Add PHY...
1194
  }
11d8f3dda   Florian Fainelli   net: dsa: Add PHY...
1195

4fa7b7188   Vivien Didelot   net: dsa: better ...
1196
  static int dsa_slave_phy_setup(struct net_device *slave_dev)
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1197
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
1198
  	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
d945097bb   Vivien Didelot   net: dsa: add sla...
1199
1200
  	struct device_node *port_dn = dp->dn;
  	struct dsa_switch *ds = dp->ds;
6819563e6   Florian Fainelli   net: dsa: allow s...
1201
  	u32 phy_flags = 0;
19334920e   Guenter Roeck   net: dsa: Set val...
1202
  	int mode, ret;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1203

19334920e   Guenter Roeck   net: dsa: Set val...
1204
1205
1206
  	mode = of_get_phy_mode(port_dn);
  	if (mode < 0)
  		mode = PHY_INTERFACE_MODE_NA;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1207

44cc27e43   Ioana Ciornei   net: phylink: Add...
1208
1209
1210
1211
  	dp->pl_config.dev = &slave_dev->dev;
  	dp->pl_config.type = PHYLINK_NETDEV;
  
  	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
77373d49d   Ioana Ciornei   net: dsa: Move th...
1212
  				&dsa_port_phylink_mac_ops);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1213
1214
1215
1216
1217
  	if (IS_ERR(dp->pl)) {
  		netdev_err(slave_dev,
  			   "error creating PHYLINK: %ld
  ", PTR_ERR(dp->pl));
  		return PTR_ERR(dp->pl);
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1218
  	}
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1219
1220
1221
1222
1223
1224
  	/* Register only if the switch provides such a callback, since this
  	 * callback takes precedence over polling the link GPIO in PHYLINK
  	 * (see phylink_get_fixed_state).
  	 */
  	if (ds->ops->phylink_fixed_state)
  		phylink_fixed_state_cb(dp->pl, dsa_slave_phylink_fixed_state);
9d490b4ee   Vivien Didelot   net: dsa: rename ...
1225
  	if (ds->ops->get_phy_flags)
d945097bb   Vivien Didelot   net: dsa: add sla...
1226
  		phy_flags = ds->ops->get_phy_flags(ds, dp->index);
6819563e6   Florian Fainelli   net: dsa: allow s...
1227

aab9c4067   Florian Fainelli   net: dsa: Plug in...
1228
  	ret = phylink_of_phy_connect(dp->pl, port_dn, phy_flags);
6146dd453   Vladimir Oltean   net: dsa: Avoid n...
1229
1230
1231
  	if (ret == -ENODEV && ds->slave_mii_bus) {
  		/* We could not connect to a designated PHY or SFP, so try to
  		 * use the switch internal MDIO bus instead
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1232
  		 */
d945097bb   Vivien Didelot   net: dsa: add sla...
1233
  		ret = dsa_slave_phy_connect(slave_dev, dp->index);
d25b8e742   Russell King   net: dsa: better ...
1234
  		if (ret) {
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1235
1236
1237
  			netdev_err(slave_dev,
  				   "failed to connect to port %d: %d
  ",
d945097bb   Vivien Didelot   net: dsa: add sla...
1238
  				   dp->index, ret);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1239
  			phylink_destroy(dp->pl);
c305c1651   Florian Fainelli   net: dsa: move PH...
1240
  			return ret;
d25b8e742   Russell King   net: dsa: better ...
1241
  		}
b31f65fb4   Andrew Lunn   net: dsa: slave: ...
1242
  	}
9697f1cde   Florian Fainelli   net: dsa: propaga...
1243

6146dd453   Vladimir Oltean   net: dsa: Avoid n...
1244
  	return ret;
0d8bcdd38   Florian Fainelli   net: dsa: allow f...
1245
  }
244625491   Florian Fainelli   net: dsa: allow s...
1246
1247
  int dsa_slave_suspend(struct net_device *slave_dev)
  {
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1248
  	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
244625491   Florian Fainelli   net: dsa: allow s...
1249

a94c689e6   Florian Fainelli   net: dsa: Do not ...
1250
1251
  	if (!netif_running(slave_dev))
  		return 0;
97a69a0de   Vladimir Oltean   net: dsa: Add sup...
1252
1253
  	cancel_work_sync(&dp->xmit_work);
  	skb_queue_purge(&dp->xmit_queue);
f154be241   Florian Fainelli   net: dsa: Bring b...
1254
  	netif_device_detach(slave_dev);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1255
1256
1257
  	rtnl_lock();
  	phylink_stop(dp->pl);
  	rtnl_unlock();
244625491   Florian Fainelli   net: dsa: allow s...
1258
1259
1260
1261
1262
1263
  
  	return 0;
  }
  
  int dsa_slave_resume(struct net_device *slave_dev)
  {
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1264
  	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
a94c689e6   Florian Fainelli   net: dsa: Do not ...
1265
1266
  	if (!netif_running(slave_dev))
  		return 0;
244625491   Florian Fainelli   net: dsa: allow s...
1267
  	netif_device_attach(slave_dev);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1268
1269
1270
  	rtnl_lock();
  	phylink_start(dp->pl);
  	rtnl_unlock();
244625491   Florian Fainelli   net: dsa: allow s...
1271
1272
1273
  
  	return 0;
  }
6158eaa7a   Vivien Didelot   net: dsa: add sla...
1274
1275
  static void dsa_slave_notify(struct net_device *dev, unsigned long val)
  {
d0006b002   Vivien Didelot   net: dsa: add sla...
1276
  	struct net_device *master = dsa_slave_to_master(dev);
d945097bb   Vivien Didelot   net: dsa: add sla...
1277
  	struct dsa_port *dp = dsa_slave_to_port(dev);
6158eaa7a   Vivien Didelot   net: dsa: add sla...
1278
1279
1280
1281
1282
1283
1284
1285
1286
  	struct dsa_notifier_register_info rinfo = {
  		.switch_number = dp->ds->index,
  		.port_number = dp->index,
  		.master = master,
  		.info.dev = dev,
  	};
  
  	call_dsa_notifiers(val, dev, &rinfo.info);
  }
951259aa6   Vivien Didelot   net: dsa: remove ...
1287
  int dsa_slave_create(struct dsa_port *port)
91da11f87   Lennert Buytenhek   net: Distributed ...
1288
  {
24a9332a5   Vivien Didelot   net: dsa: constif...
1289
  	const struct dsa_port *cpu_dp = port->cpu_dp;
f8b8b1cd5   Vivien Didelot   net: dsa: split d...
1290
  	struct net_device *master = cpu_dp->master;
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1291
  	struct dsa_switch *ds = port->ds;
951259aa6   Vivien Didelot   net: dsa: remove ...
1292
  	const char *name = port->name;
91da11f87   Lennert Buytenhek   net: Distributed ...
1293
1294
1295
  	struct net_device *slave_dev;
  	struct dsa_slave_priv *p;
  	int ret;
55199df6d   Florian Fainelli   net: dsa: Allow s...
1296
1297
1298
1299
1300
1301
  	if (!ds->num_tx_queues)
  		ds->num_tx_queues = 1;
  
  	slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name,
  				     NET_NAME_UNKNOWN, ether_setup,
  				     ds->num_tx_queues, 1);
91da11f87   Lennert Buytenhek   net: Distributed ...
1302
  	if (slave_dev == NULL)
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1303
  		return -ENOMEM;
91da11f87   Lennert Buytenhek   net: Distributed ...
1304

9b236d2a6   Vladimir Oltean   net: dsa: Adverti...
1305
1306
1307
  	slave_dev->features = master->vlan_features | NETIF_F_HW_TC;
  	if (ds->ops->port_vlan_add && ds->ops->port_vlan_del)
  		slave_dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
f50f21274   Florian Fainelli   net: dsa: Add plu...
1308
  	slave_dev->hw_features |= NETIF_F_HW_TC;
7ad24ea4b   Wilfried Klaebe   net: get rid of S...
1309
  	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
4974f9b7e   Petr Å tetiar   net: dsa: support...
1310
  	if (!IS_ERR_OR_NULL(port->mac))
a2c7023f7   Xiaofei Shen   net: dsa: read ma...
1311
1312
1313
  		ether_addr_copy(slave_dev->dev_addr, port->mac);
  	else
  		eth_hw_addr_inherit(slave_dev, master);
0a5f107b6   Phil Sutter   net: dsa: convert...
1314
  	slave_dev->priv_flags |= IFF_NO_QUEUE;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
1315
  	slave_dev->netdev_ops = &dsa_slave_netdev_ops;
8b1efc0f8   Jarod Wilson   net: remove MTU l...
1316
1317
  	slave_dev->min_mtu = 0;
  	slave_dev->max_mtu = ETH_MAX_MTU;
f37db85d0   Florian Fainelli   net: dsa: Set a "...
1318
  	SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
d442ad4ab   Stephen Hemminger   dsa: convert to n...
1319

4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1320
1321
  	SET_NETDEV_DEV(slave_dev, port->ds->dev);
  	slave_dev->dev.of_node = port->dn;
5075314e4   Alexander Duyck   dsa: Split ops up...
1322
1323
1324
  	slave_dev->vlan_features = master->vlan_features;
  
  	p = netdev_priv(slave_dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
1325
1326
1327
1328
1329
  	p->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
  	if (!p->stats64) {
  		free_netdev(slave_dev);
  		return -ENOMEM;
  	}
4cfbf09cf   Vivien Didelot   net: dsa: remove ...
1330
  	p->dp = port;
f50f21274   Florian Fainelli   net: dsa: Add plu...
1331
  	INIT_LIST_HEAD(&p->mall_tc_list);
97a69a0de   Vladimir Oltean   net: dsa: Add sup...
1332
1333
  	INIT_WORK(&port->xmit_work, dsa_port_xmit_work);
  	skb_queue_head_init(&port->xmit_queue);
152402483   Vivien Didelot   net: dsa: add tag...
1334
  	p->xmit = cpu_dp->tag_ops->xmit;
f8b8b1cd5   Vivien Didelot   net: dsa: split d...
1335
  	port->slave = slave_dev;
91da11f87   Lennert Buytenhek   net: Distributed ...
1336
1337
  
  	netif_carrier_off(slave_dev);
4fa7b7188   Vivien Didelot   net: dsa: better ...
1338
  	ret = dsa_slave_phy_setup(slave_dev);
0071f56e4   Andrew Lunn   dsa: Register net...
1339
1340
1341
  	if (ret) {
  		netdev_err(master, "error %d setting up slave phy
  ", ret);
e804441cf   Florian Fainelli   net: dsa: Fix net...
1342
1343
  		goto out_free;
  	}
6158eaa7a   Vivien Didelot   net: dsa: add sla...
1344
  	dsa_slave_notify(slave_dev, DSA_PORT_REGISTER);
60724d4ba   Florian Fainelli   net: dsa: Add sup...
1345

e804441cf   Florian Fainelli   net: dsa: Fix net...
1346
1347
1348
1349
1350
1351
  	ret = register_netdev(slave_dev);
  	if (ret) {
  		netdev_err(master, "error %d registering interface %s
  ",
  			   ret, slave_dev->name);
  		goto out_phy;
0071f56e4   Andrew Lunn   dsa: Register net...
1352
  	}
d87d6f44d   Guenter Roeck   net: dsa: Ensure ...
1353
  	return 0;
e804441cf   Florian Fainelli   net: dsa: Fix net...
1354
1355
  
  out_phy:
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1356
1357
1358
1359
  	rtnl_lock();
  	phylink_disconnect_phy(p->dp->pl);
  	rtnl_unlock();
  	phylink_destroy(p->dp->pl);
e804441cf   Florian Fainelli   net: dsa: Fix net...
1360
1361
1362
  out_free:
  	free_percpu(p->stats64);
  	free_netdev(slave_dev);
f8b8b1cd5   Vivien Didelot   net: dsa: split d...
1363
  	port->slave = NULL;
e804441cf   Florian Fainelli   net: dsa: Fix net...
1364
  	return ret;
91da11f87   Lennert Buytenhek   net: Distributed ...
1365
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1366

cda5c15b2   Neil Armstrong   net: dsa: move ds...
1367
1368
  void dsa_slave_destroy(struct net_device *slave_dev)
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
1369
  	struct dsa_port *dp = dsa_slave_to_port(slave_dev);
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1370
1371
1372
  	struct dsa_slave_priv *p = netdev_priv(slave_dev);
  
  	netif_carrier_off(slave_dev);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1373
1374
1375
  	rtnl_lock();
  	phylink_disconnect_phy(dp->pl);
  	rtnl_unlock();
881eadabe   Johan Hovold   net: dsa: slave: ...
1376

6158eaa7a   Vivien Didelot   net: dsa: add sla...
1377
  	dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1378
  	unregister_netdev(slave_dev);
aab9c4067   Florian Fainelli   net: dsa: Plug in...
1379
  	phylink_destroy(dp->pl);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
1380
  	free_percpu(p->stats64);
cda5c15b2   Neil Armstrong   net: dsa: move ds...
1381
1382
  	free_netdev(slave_dev);
  }
f3b78049d   Vivien Didelot   net: dsa: make ds...
1383
  static bool dsa_slave_dev_check(const struct net_device *dev)
b73adef67   Florian Fainelli   net: dsa: integra...
1384
1385
1386
  {
  	return dev->netdev_ops == &dsa_slave_netdev_ops;
  }
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1387
1388
  static int dsa_slave_changeupper(struct net_device *dev,
  				 struct netdev_notifier_changeupper_info *info)
b73adef67   Florian Fainelli   net: dsa: integra...
1389
  {
d945097bb   Vivien Didelot   net: dsa: add sla...
1390
  	struct dsa_port *dp = dsa_slave_to_port(dev);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1391
  	int err = NOTIFY_DONE;
b73adef67   Florian Fainelli   net: dsa: integra...
1392

8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1393
1394
  	if (netif_is_bridge_master(info->upper_dev)) {
  		if (info->linking) {
17d7802b7   Vivien Didelot   net: dsa: change ...
1395
  			err = dsa_port_bridge_join(dp, info->upper_dev);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1396
1397
  			err = notifier_from_errno(err);
  		} else {
17d7802b7   Vivien Didelot   net: dsa: change ...
1398
  			dsa_port_bridge_leave(dp, info->upper_dev);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1399
  			err = NOTIFY_OK;
6debb68a2   Vivien Didelot   net: dsa: refine ...
1400
  		}
6debb68a2   Vivien Didelot   net: dsa: refine ...
1401
  	}
b73adef67   Florian Fainelli   net: dsa: integra...
1402

8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1403
  	return err;
6debb68a2   Vivien Didelot   net: dsa: refine ...
1404
  }
b73adef67   Florian Fainelli   net: dsa: integra...
1405

cc1d5bda1   Florian Fainelli   net: dsa: Deny en...
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
  static int dsa_slave_upper_vlan_check(struct net_device *dev,
  				      struct netdev_notifier_changeupper_info *
  				      info)
  {
  	struct netlink_ext_ack *ext_ack;
  	struct net_device *slave;
  	struct dsa_port *dp;
  
  	ext_ack = netdev_notifier_info_to_extack(&info->info);
  
  	if (!is_vlan_dev(dev))
  		return NOTIFY_DONE;
  
  	slave = vlan_dev_real_dev(dev);
  	if (!dsa_slave_dev_check(slave))
  		return NOTIFY_DONE;
  
  	dp = dsa_slave_to_port(slave);
  	if (!dp->bridge_dev)
  		return NOTIFY_DONE;
  
  	/* Deny enslaving a VLAN device into a VLAN-aware bridge */
  	if (br_vlan_enabled(dp->bridge_dev) &&
  	    netif_is_bridge_master(info->upper_dev) && info->linking) {
  		NL_SET_ERR_MSG_MOD(ext_ack,
  				   "Cannot enslave VLAN device into VLAN aware bridge");
  		return notifier_from_errno(-EINVAL);
  	}
  
  	return NOTIFY_DONE;
  }
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1437
1438
  static int dsa_slave_netdevice_event(struct notifier_block *nb,
  				     unsigned long event, void *ptr)
6debb68a2   Vivien Didelot   net: dsa: refine ...
1439
1440
  {
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
cc1d5bda1   Florian Fainelli   net: dsa: Deny en...
1441
1442
1443
  	if (event == NETDEV_CHANGEUPPER) {
  		if (!dsa_slave_dev_check(dev))
  			return dsa_slave_upper_vlan_check(dev, ptr);
8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1444

8e92ab3a4   Vivien Didelot   net: dsa: simplif...
1445
  		return dsa_slave_changeupper(dev, ptr);
cc1d5bda1   Florian Fainelli   net: dsa: Deny en...
1446
  	}
b73adef67   Florian Fainelli   net: dsa: integra...
1447

b73adef67   Florian Fainelli   net: dsa: integra...
1448
1449
  	return NOTIFY_DONE;
  }
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1450

c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
  struct dsa_switchdev_event_work {
  	struct work_struct work;
  	struct switchdev_notifier_fdb_info fdb_info;
  	struct net_device *dev;
  	unsigned long event;
  };
  
  static void dsa_slave_switchdev_event_work(struct work_struct *work)
  {
  	struct dsa_switchdev_event_work *switchdev_work =
  		container_of(work, struct dsa_switchdev_event_work, work);
  	struct net_device *dev = switchdev_work->dev;
  	struct switchdev_notifier_fdb_info *fdb_info;
d945097bb   Vivien Didelot   net: dsa: add sla...
1464
  	struct dsa_port *dp = dsa_slave_to_port(dev);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1465
1466
1467
1468
1469
1470
  	int err;
  
  	rtnl_lock();
  	switch (switchdev_work->event) {
  	case SWITCHDEV_FDB_ADD_TO_DEVICE:
  		fdb_info = &switchdev_work->fdb_info;
a37fb855f   Vivien Didelot   net: dsa: fix add...
1471
1472
  		if (!fdb_info->added_by_user)
  			break;
d945097bb   Vivien Didelot   net: dsa: add sla...
1473
  		err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1474
1475
1476
1477
1478
  		if (err) {
  			netdev_dbg(dev, "fdb add failed err=%d
  ", err);
  			break;
  		}
e9ba0fbc7   Ido Schimmel   bridge: switchdev...
1479
  		fdb_info->offloaded = true;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1480
  		call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
6685987c2   Petr Machata   switchdev: Add ex...
1481
  					 &fdb_info->info, NULL);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1482
1483
1484
1485
  		break;
  
  	case SWITCHDEV_FDB_DEL_TO_DEVICE:
  		fdb_info = &switchdev_work->fdb_info;
a37fb855f   Vivien Didelot   net: dsa: fix add...
1486
1487
  		if (!fdb_info->added_by_user)
  			break;
d945097bb   Vivien Didelot   net: dsa: add sla...
1488
  		err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
  		if (err) {
  			netdev_dbg(dev, "fdb del failed err=%d
  ", err);
  			dev_close(dev);
  		}
  		break;
  	}
  	rtnl_unlock();
  
  	kfree(switchdev_work->fdb_info.addr);
  	kfree(switchdev_work);
  	dev_put(dev);
  }
  
  static int
  dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
  				  switchdev_work,
  				  const struct switchdev_notifier_fdb_info *
  				  fdb_info)
  {
  	memcpy(&switchdev_work->fdb_info, fdb_info,
  	       sizeof(switchdev_work->fdb_info));
  	switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
  	if (!switchdev_work->fdb_info.addr)
  		return -ENOMEM;
  	ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
  			fdb_info->addr);
  	return 0;
  }
  
  /* Called under rcu_read_lock() */
  static int dsa_slave_switchdev_event(struct notifier_block *unused,
  				     unsigned long event, void *ptr)
  {
  	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
  	struct dsa_switchdev_event_work *switchdev_work;
79b139f4b   Vivien Didelot   net: dsa: use swi...
1525
1526
1527
1528
1529
1530
1531
1532
  	int err;
  
  	if (event == SWITCHDEV_PORT_ATTR_SET) {
  		err = switchdev_handle_port_attr_set(dev, ptr,
  						     dsa_slave_dev_check,
  						     dsa_slave_port_attr_set);
  		return notifier_from_errno(err);
  	}
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
  
  	if (!dsa_slave_dev_check(dev))
  		return NOTIFY_DONE;
  
  	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
  	if (!switchdev_work)
  		return NOTIFY_BAD;
  
  	INIT_WORK(&switchdev_work->work,
  		  dsa_slave_switchdev_event_work);
  	switchdev_work->dev = dev;
  	switchdev_work->event = event;
  
  	switch (event) {
  	case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
  	case SWITCHDEV_FDB_DEL_TO_DEVICE:
a37fb855f   Vivien Didelot   net: dsa: fix add...
1549
  		if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr))
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
  			goto err_fdb_work_init;
  		dev_hold(dev);
  		break;
  	default:
  		kfree(switchdev_work);
  		return NOTIFY_DONE;
  	}
  
  	dsa_schedule_work(&switchdev_work->work);
  	return NOTIFY_OK;
  
  err_fdb_work_init:
  	kfree(switchdev_work);
  	return NOTIFY_BAD;
  }
2b239f678   Petr Machata   net: dsa: slave: ...
1565
1566
1567
1568
  static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
  					      unsigned long event, void *ptr)
  {
  	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
79b139f4b   Vivien Didelot   net: dsa: use swi...
1569
  	int err;
2b239f678   Petr Machata   net: dsa: slave: ...
1570
1571
  
  	switch (event) {
79b139f4b   Vivien Didelot   net: dsa: use swi...
1572
1573
1574
1575
1576
  	case SWITCHDEV_PORT_OBJ_ADD:
  		err = switchdev_handle_port_obj_add(dev, ptr,
  						    dsa_slave_dev_check,
  						    dsa_slave_port_obj_add);
  		return notifier_from_errno(err);
2b239f678   Petr Machata   net: dsa: slave: ...
1577
  	case SWITCHDEV_PORT_OBJ_DEL:
79b139f4b   Vivien Didelot   net: dsa: use swi...
1578
1579
1580
1581
  		err = switchdev_handle_port_obj_del(dev, ptr,
  						    dsa_slave_dev_check,
  						    dsa_slave_port_obj_del);
  		return notifier_from_errno(err);
9ed1eced2   Florian Fainelli   net: dsa: Handle ...
1582
  	case SWITCHDEV_PORT_ATTR_SET:
79b139f4b   Vivien Didelot   net: dsa: use swi...
1583
1584
1585
1586
  		err = switchdev_handle_port_attr_set(dev, ptr,
  						     dsa_slave_dev_check,
  						     dsa_slave_port_attr_set);
  		return notifier_from_errno(err);
2b239f678   Petr Machata   net: dsa: slave: ...
1587
1588
1589
1590
  	}
  
  	return NOTIFY_DONE;
  }
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1591
  static struct notifier_block dsa_slave_nb __read_mostly = {
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1592
1593
1594
1595
1596
  	.notifier_call  = dsa_slave_netdevice_event,
  };
  
  static struct notifier_block dsa_slave_switchdev_notifier = {
  	.notifier_call = dsa_slave_switchdev_event,
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1597
  };
2b239f678   Petr Machata   net: dsa: slave: ...
1598
1599
1600
  static struct notifier_block dsa_slave_switchdev_blocking_notifier = {
  	.notifier_call = dsa_slave_switchdev_blocking_event,
  };
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1601
1602
  int dsa_slave_register_notifier(void)
  {
2b239f678   Petr Machata   net: dsa: slave: ...
1603
  	struct notifier_block *nb;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1604
1605
1606
1607
1608
1609
1610
1611
1612
  	int err;
  
  	err = register_netdevice_notifier(&dsa_slave_nb);
  	if (err)
  		return err;
  
  	err = register_switchdev_notifier(&dsa_slave_switchdev_notifier);
  	if (err)
  		goto err_switchdev_nb;
2b239f678   Petr Machata   net: dsa: slave: ...
1613
1614
1615
1616
  	nb = &dsa_slave_switchdev_blocking_notifier;
  	err = register_switchdev_blocking_notifier(nb);
  	if (err)
  		goto err_switchdev_blocking_nb;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1617
  	return 0;
2b239f678   Petr Machata   net: dsa: slave: ...
1618
1619
  err_switchdev_blocking_nb:
  	unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1620
1621
1622
  err_switchdev_nb:
  	unregister_netdevice_notifier(&dsa_slave_nb);
  	return err;
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1623
1624
1625
1626
  }
  
  void dsa_slave_unregister_notifier(void)
  {
2b239f678   Petr Machata   net: dsa: slave: ...
1627
  	struct notifier_block *nb;
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1628
  	int err;
2b239f678   Petr Machata   net: dsa: slave: ...
1629
1630
1631
1632
1633
  	nb = &dsa_slave_switchdev_blocking_notifier;
  	err = unregister_switchdev_blocking_notifier(nb);
  	if (err)
  		pr_err("DSA: failed to unregister switchdev blocking notifier (%d)
  ", err);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
1634
1635
1636
1637
  	err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier);
  	if (err)
  		pr_err("DSA: failed to unregister switchdev notifier (%d)
  ", err);
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
1638
1639
1640
1641
1642
  	err = unregister_netdevice_notifier(&dsa_slave_nb);
  	if (err)
  		pr_err("DSA: failed to unregister slave notifier (%d)
  ", err);
  }