Blame view

net/dsa/dsa.c 8.08 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
91da11f87   Lennert Buytenhek   net: Distributed ...
2
3
  /*
   * net/dsa/dsa.c - Hardware switch handling
e84665c9c   Lennert Buytenhek   dsa: add switch c...
4
   * Copyright (c) 2008-2009 Marvell Semiconductor
5e95329b7   Florian Fainelli   dsa: add device t...
5
   * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
91da11f87   Lennert Buytenhek   net: Distributed ...
6
   */
51579c3f1   Guenter Roeck   net: dsa: Add sup...
7
  #include <linux/device.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
8
  #include <linux/list.h>
91da11f87   Lennert Buytenhek   net: Distributed ...
9
  #include <linux/platform_device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
10
  #include <linux/slab.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
11
  #include <linux/module.h>
60724d4ba   Florian Fainelli   net: dsa: Add sup...
12
  #include <linux/notifier.h>
5e95329b7   Florian Fainelli   dsa: add device t...
13
14
15
  #include <linux/of.h>
  #include <linux/of_mdio.h>
  #include <linux/of_platform.h>
769a02028   Florian Fainelli   net: dsa: utilize...
16
  #include <linux/of_net.h>
c6e970a04   Andrew Lunn   net: break includ...
17
  #include <linux/netdevice.h>
51579c3f1   Guenter Roeck   net: dsa: Add sup...
18
  #include <linux/sysfs.h>
cbc5d90b3   Neil Armstrong   net: dsa: complet...
19
  #include <linux/phy_fixed.h>
90af1059c   Brandon Streiff   net: dsa: forward...
20
  #include <linux/ptp_classify.h>
a86d8becc   Florian Fainelli   net: dsa: Factor ...
21
  #include <linux/etherdevice.h>
ea5dd34be   Vivien Didelot   net: dsa: include...
22

91da11f87   Lennert Buytenhek   net: Distributed ...
23
  #include "dsa_priv.h"
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
24
25
  static LIST_HEAD(dsa_tag_drivers_list);
  static DEFINE_MUTEX(dsa_tag_drivers_lock);
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
26
27
28
29
30
31
32
33
  static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb,
  					    struct net_device *dev)
  {
  	/* Just return the original SKB */
  	return skb;
  }
  
  static const struct dsa_device_ops none_ops = {
875138f81   Andrew Lunn   dsa: Move tagger ...
34
  	.name	= "none",
056eed2fb   Andrew Lunn   dsa: Add TAG prot...
35
  	.proto	= DSA_TAG_PROTO_NONE,
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
36
37
38
  	.xmit	= dsa_slave_notag_xmit,
  	.rcv	= NULL,
  };
409065b06   Andrew Lunn   dsa: Register the...
39
  DSA_TAG_DRIVER(none_ops);
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
40
41
42
43
44
45
46
47
48
  static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver,
  				    struct module *owner)
  {
  	dsa_tag_driver->owner = owner;
  
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list);
  	mutex_unlock(&dsa_tag_drivers_lock);
  }
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
49
50
51
  void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[],
  			      unsigned int count, struct module *owner)
  {
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
52
53
54
55
56
57
58
59
60
61
62
  	unsigned int i;
  
  	for (i = 0; i < count; i++)
  		dsa_tag_driver_register(dsa_tag_driver_array[i], owner);
  }
  
  static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver)
  {
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_del(&dsa_tag_driver->list);
  	mutex_unlock(&dsa_tag_drivers_lock);
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
63
64
65
66
67
68
  }
  EXPORT_SYMBOL_GPL(dsa_tag_drivers_register);
  
  void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[],
  				unsigned int count)
  {
bdc6fe5bb   Andrew Lunn   dsa: Keep link li...
69
70
71
72
  	unsigned int i;
  
  	for (i = 0; i < count; i++)
  		dsa_tag_driver_unregister(dsa_tag_driver_array[i]);
d3b8c0498   Andrew Lunn   dsa: Add boilerpl...
73
74
  }
  EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister);
98cdb4807   Florian Fainelli   net: dsa: Expose ...
75
76
  const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)
  {
875138f81   Andrew Lunn   dsa: Move tagger ...
77
  	return ops->name;
98cdb4807   Florian Fainelli   net: dsa: Expose ...
78
  };
c39e2a1d7   Andrew Lunn   dsa: Rename dsa_r...
79
  const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol)
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
80
  {
367561753   Andrew Lunn   dsa: Make use of ...
81
  	struct dsa_tag_driver *dsa_tag_driver;
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
82
  	const struct dsa_device_ops *ops;
367561753   Andrew Lunn   dsa: Make use of ...
83
84
  	char module_name[128];
  	bool found = false;
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
85

367561753   Andrew Lunn   dsa: Make use of ...
86
87
  	snprintf(module_name, 127, "%s%d", DSA_TAG_DRIVER_ALIAS,
  		 tag_protocol);
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
88

367561753   Andrew Lunn   dsa: Make use of ...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  	request_module(module_name);
  
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
  		ops = dsa_tag_driver->ops;
  		if (ops->proto == tag_protocol) {
  			found = true;
  			break;
  		}
  	}
  
  	if (found) {
  		if (!try_module_get(dsa_tag_driver->owner))
  			ops = ERR_PTR(-ENOPROTOOPT);
  	} else {
  		ops = ERR_PTR(-ENOPROTOOPT);
  	}
  
  	mutex_unlock(&dsa_tag_drivers_lock);
39a7f2a4e   Andrew Lunn   net: dsa: Refacto...
108
109
110
  
  	return ops;
  }
4dad81ee1   Andrew Lunn   dsa: Add stub tag...
111
112
  void dsa_tag_driver_put(const struct dsa_device_ops *ops)
  {
367561753   Andrew Lunn   dsa: Make use of ...
113
114
115
116
117
118
119
120
121
122
  	struct dsa_tag_driver *dsa_tag_driver;
  
  	mutex_lock(&dsa_tag_drivers_lock);
  	list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
  		if (dsa_tag_driver->ops == ops) {
  			module_put(dsa_tag_driver->owner);
  			break;
  		}
  	}
  	mutex_unlock(&dsa_tag_drivers_lock);
4dad81ee1   Andrew Lunn   dsa: Add stub tag...
123
  }
91da11f87   Lennert Buytenhek   net: Distributed ...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  static int dev_is_class(struct device *dev, void *class)
  {
  	if (dev->class != NULL && !strcmp(dev->class->name, class))
  		return 1;
  
  	return 0;
  }
  
  static struct device *dev_find_class(struct device *parent, char *class)
  {
  	if (dev_is_class(parent, class)) {
  		get_device(parent);
  		return parent;
  	}
  
  	return device_find_child(parent, class, dev_is_class);
  }
14b89f36e   Florian Fainelli   net: dsa: Rename ...
141
  struct net_device *dsa_dev_to_net_device(struct device *dev)
91da11f87   Lennert Buytenhek   net: Distributed ...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  {
  	struct device *d;
  
  	d = dev_find_class(dev, "net");
  	if (d != NULL) {
  		struct net_device *nd;
  
  		nd = to_net_dev(d);
  		dev_hold(nd);
  		put_device(d);
  
  		return nd;
  	}
  
  	return NULL;
  }
14b89f36e   Florian Fainelli   net: dsa: Rename ...
158
  EXPORT_SYMBOL_GPL(dsa_dev_to_net_device);
91da11f87   Lennert Buytenhek   net: Distributed ...
159

90af1059c   Brandon Streiff   net: dsa: forward...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  /* Determine if we should defer delivery of skb until we have a rx timestamp.
   *
   * Called from dsa_switch_rcv. For now, this will only work if tagging is
   * enabled on the switch. Normally the MAC driver would retrieve the hardware
   * timestamp when it reads the packet out of the hardware. However in a DSA
   * switch, the DSA driver owning the interface to which the packet is
   * delivered is never notified unless we do so here.
   */
  static bool dsa_skb_defer_rx_timestamp(struct dsa_slave_priv *p,
  				       struct sk_buff *skb)
  {
  	struct dsa_switch *ds = p->dp->ds;
  	unsigned int type;
  
  	if (skb_headroom(skb) < ETH_HLEN)
  		return false;
  
  	__skb_push(skb, ETH_HLEN);
  
  	type = ptp_classify_raw(skb);
  
  	__skb_pull(skb, ETH_HLEN);
  
  	if (type == PTP_CLASS_NONE)
  		return false;
  
  	if (likely(ds->ops->port_rxtstamp))
  		return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type);
  
  	return false;
  }
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
191
  static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
89e49506b   Florian Westphal   dsa: remove unuse...
192
  			  struct packet_type *pt, struct net_device *unused)
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
193
  {
2f657a600   Vivien Didelot   net: dsa: change ...
194
  	struct dsa_port *cpu_dp = dev->dsa_ptr;
a86d8becc   Florian Fainelli   net: dsa: Factor ...
195
  	struct sk_buff *nskb = NULL;
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
196
  	struct pcpu_sw_netstats *s;
f613ed665   Florian Fainelli   net: dsa: Add sup...
197
  	struct dsa_slave_priv *p;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
198

2f657a600   Vivien Didelot   net: dsa: change ...
199
  	if (unlikely(!cpu_dp)) {
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
200
201
202
  		kfree_skb(skb);
  		return 0;
  	}
16c5dcb13   Florian Fainelli   net: dsa: Move sk...
203
204
205
  	skb = skb_unshare(skb, GFP_ATOMIC);
  	if (!skb)
  		return 0;
2f657a600   Vivien Didelot   net: dsa: change ...
206
  	nskb = cpu_dp->rcv(skb, dev, pt);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
207
208
209
210
211
212
  	if (!nskb) {
  		kfree_skb(skb);
  		return 0;
  	}
  
  	skb = nskb;
f613ed665   Florian Fainelli   net: dsa: Add sup...
213
  	p = netdev_priv(skb->dev);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
214
215
216
  	skb_push(skb, ETH_HLEN);
  	skb->pkt_type = PACKET_HOST;
  	skb->protocol = eth_type_trans(skb, skb->dev);
5f6b4e14c   Florian Fainelli   net: dsa: User pe...
217
218
219
220
221
  	s = this_cpu_ptr(p->stats64);
  	u64_stats_update_begin(&s->syncp);
  	s->rx_packets++;
  	s->rx_bytes += skb->len;
  	u64_stats_update_end(&s->syncp);
a86d8becc   Florian Fainelli   net: dsa: Factor ...
222

90af1059c   Brandon Streiff   net: dsa: forward...
223
224
  	if (dsa_skb_defer_rx_timestamp(p, skb))
  		return 0;
a86d8becc   Florian Fainelli   net: dsa: Factor ...
225
226
227
  	netif_receive_skb(skb);
  
  	return 0;
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
228
  }
ac2629a47   Florian Fainelli   net: dsa: Move ds...
229
  #ifdef CONFIG_PM_SLEEP
e7d53ad32   Vivien Didelot   net: dsa: unexpor...
230
231
  static bool dsa_is_port_initialized(struct dsa_switch *ds, int p)
  {
4a5b85ffe   Vivien Didelot   net: dsa: use dsa...
232
  	return dsa_is_user_port(ds, p) && ds->ports[p].slave;
e7d53ad32   Vivien Didelot   net: dsa: unexpor...
233
  }
ac2629a47   Florian Fainelli   net: dsa: Move ds...
234
235
236
237
238
239
240
241
  int dsa_switch_suspend(struct dsa_switch *ds)
  {
  	int i, ret = 0;
  
  	/* Suspend slave network devices */
  	for (i = 0; i < ds->num_ports; i++) {
  		if (!dsa_is_port_initialized(ds, i))
  			continue;
f8b8b1cd5   Vivien Didelot   net: dsa: split d...
242
  		ret = dsa_slave_suspend(ds->ports[i].slave);
ac2629a47   Florian Fainelli   net: dsa: Move ds...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  		if (ret)
  			return ret;
  	}
  
  	if (ds->ops->suspend)
  		ret = ds->ops->suspend(ds);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dsa_switch_suspend);
  
  int dsa_switch_resume(struct dsa_switch *ds)
  {
  	int i, ret = 0;
  
  	if (ds->ops->resume)
  		ret = ds->ops->resume(ds);
  
  	if (ret)
  		return ret;
  
  	/* Resume slave network devices */
  	for (i = 0; i < ds->num_ports; i++) {
  		if (!dsa_is_port_initialized(ds, i))
  			continue;
f8b8b1cd5   Vivien Didelot   net: dsa: split d...
268
  		ret = dsa_slave_resume(ds->ports[i].slave);
ac2629a47   Florian Fainelli   net: dsa: Move ds...
269
270
271
272
273
274
275
276
  		if (ret)
  			return ret;
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(dsa_switch_resume);
  #endif
61b7363ff   Florian Fainelli   net: dsa: make ds...
277
  static struct packet_type dsa_pack_type __read_mostly = {
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
278
279
280
  	.type	= cpu_to_be16(ETH_P_XDSA),
  	.func	= dsa_switch_rcv,
  };
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
281
282
283
284
285
286
  static struct workqueue_struct *dsa_owq;
  
  bool dsa_schedule_work(struct work_struct *work)
  {
  	return queue_work(dsa_owq, work);
  }
60724d4ba   Florian Fainelli   net: dsa: Add sup...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  static ATOMIC_NOTIFIER_HEAD(dsa_notif_chain);
  
  int register_dsa_notifier(struct notifier_block *nb)
  {
  	return atomic_notifier_chain_register(&dsa_notif_chain, nb);
  }
  EXPORT_SYMBOL_GPL(register_dsa_notifier);
  
  int unregister_dsa_notifier(struct notifier_block *nb)
  {
  	return atomic_notifier_chain_unregister(&dsa_notif_chain, nb);
  }
  EXPORT_SYMBOL_GPL(unregister_dsa_notifier);
  
  int call_dsa_notifiers(unsigned long val, struct net_device *dev,
  		       struct dsa_notifier_info *info)
  {
  	info->dev = dev;
  	return atomic_notifier_call_chain(&dsa_notif_chain, val, info);
  }
  EXPORT_SYMBOL_GPL(call_dsa_notifiers);
91da11f87   Lennert Buytenhek   net: Distributed ...
308
309
  static int __init dsa_init_module(void)
  {
7df899c36   Ben Hutchings   dsa: Combine core...
310
  	int rc;
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
311
312
313
314
  	dsa_owq = alloc_ordered_workqueue("dsa_ordered",
  					  WQ_MEM_RECLAIM);
  	if (!dsa_owq)
  		return -ENOMEM;
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
315
316
  	rc = dsa_slave_register_notifier();
  	if (rc)
68be93024   YueHaibing   net: dsa: Fix err...
317
  		goto register_notifier_fail;
b73adef67   Florian Fainelli   net: dsa: integra...
318

3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
319
  	dev_add_pack(&dsa_pack_type);
409065b06   Andrew Lunn   dsa: Register the...
320
321
  	dsa_tag_driver_register(&DSA_TAG_DRIVER_NAME(none_ops),
  				THIS_MODULE);
7df899c36   Ben Hutchings   dsa: Combine core...
322
  	return 0;
68be93024   YueHaibing   net: dsa: Fix err...
323

68be93024   YueHaibing   net: dsa: Fix err...
324
325
326
327
  register_notifier_fail:
  	destroy_workqueue(dsa_owq);
  
  	return rc;
91da11f87   Lennert Buytenhek   net: Distributed ...
328
329
330
331
332
  }
  module_init(dsa_init_module);
  
  static void __exit dsa_cleanup_module(void)
  {
409065b06   Andrew Lunn   dsa: Register the...
333
  	dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops));
88e4f0ca4   Vivien Didelot   net: dsa: move ne...
334
  	dsa_slave_unregister_notifier();
3e8a72d1d   Florian Fainelli   net: dsa: reduce ...
335
  	dev_remove_pack(&dsa_pack_type);
c9eb3e0f8   Arkadi Sharshevsky   net: dsa: Add sup...
336
  	destroy_workqueue(dsa_owq);
91da11f87   Lennert Buytenhek   net: Distributed ...
337
338
  }
  module_exit(dsa_cleanup_module);
577d6a7c3   Rusty Russell   module: fix missi...
339
  MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
91da11f87   Lennert Buytenhek   net: Distributed ...
340
341
342
  MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:dsa");