Blame view
net/dsa/dsa.c
11.5 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
91da11f87 net: Distributed ... |
2 3 |
/* * net/dsa/dsa.c - Hardware switch handling |
e84665c9c dsa: add switch c... |
4 |
* Copyright (c) 2008-2009 Marvell Semiconductor |
5e95329b7 dsa: add device t... |
5 |
* Copyright (c) 2013 Florian Fainelli <florian@openwrt.org> |
91da11f87 net: Distributed ... |
6 |
*/ |
51579c3f1 net: dsa: Add sup... |
7 |
#include <linux/device.h> |
91da11f87 net: Distributed ... |
8 |
#include <linux/list.h> |
91da11f87 net: Distributed ... |
9 |
#include <linux/platform_device.h> |
5a0e3ad6a include cleanup: ... |
10 |
#include <linux/slab.h> |
3a9a231d9 net: Fix files ex... |
11 |
#include <linux/module.h> |
60724d4ba net: dsa: Add sup... |
12 |
#include <linux/notifier.h> |
5e95329b7 dsa: add device t... |
13 14 15 |
#include <linux/of.h> #include <linux/of_mdio.h> #include <linux/of_platform.h> |
769a02028 net: dsa: utilize... |
16 |
#include <linux/of_net.h> |
c6e970a04 net: break includ... |
17 |
#include <linux/netdevice.h> |
51579c3f1 net: dsa: Add sup... |
18 |
#include <linux/sysfs.h> |
cbc5d90b3 net: dsa: complet... |
19 |
#include <linux/phy_fixed.h> |
90af1059c net: dsa: forward... |
20 |
#include <linux/ptp_classify.h> |
a86d8becc net: dsa: Factor ... |
21 |
#include <linux/etherdevice.h> |
ea5dd34be net: dsa: include... |
22 |
|
91da11f87 net: Distributed ... |
23 |
#include "dsa_priv.h" |
bdc6fe5bb dsa: Keep link li... |
24 25 |
static LIST_HEAD(dsa_tag_drivers_list); static DEFINE_MUTEX(dsa_tag_drivers_lock); |
39a7f2a4e 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 dsa: Move tagger ... |
34 |
.name = "none", |
056eed2fb dsa: Add TAG prot... |
35 |
.proto = DSA_TAG_PROTO_NONE, |
39a7f2a4e net: dsa: Refacto... |
36 37 38 |
.xmit = dsa_slave_notag_xmit, .rcv = NULL, }; |
409065b06 dsa: Register the... |
39 |
DSA_TAG_DRIVER(none_ops); |
bdc6fe5bb 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 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 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 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 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 dsa: Add boilerpl... |
73 74 |
} EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister); |
98cdb4807 net: dsa: Expose ... |
75 76 |
const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) { |
875138f81 dsa: Move tagger ... |
77 |
return ops->name; |
98cdb4807 net: dsa: Expose ... |
78 |
}; |
c39e2a1d7 dsa: Rename dsa_r... |
79 |
const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol) |
39a7f2a4e net: dsa: Refacto... |
80 |
{ |
367561753 dsa: Make use of ... |
81 |
struct dsa_tag_driver *dsa_tag_driver; |
39a7f2a4e net: dsa: Refacto... |
82 |
const struct dsa_device_ops *ops; |
367561753 dsa: Make use of ... |
83 |
bool found = false; |
39a7f2a4e net: dsa: Refacto... |
84 |
|
ee91a83e0 net: dsa: Simplif... |
85 |
request_module("%s%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); |
367561753 dsa: Make use of ... |
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
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 net: dsa: Refacto... |
104 105 106 |
return ops; } |
4dad81ee1 dsa: Add stub tag... |
107 108 |
void dsa_tag_driver_put(const struct dsa_device_ops *ops) { |
367561753 dsa: Make use of ... |
109 110 111 112 113 114 115 116 117 118 |
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 dsa: Add stub tag... |
119 |
} |
91da11f87 net: Distributed ... |
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
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 net: dsa: Rename ... |
137 |
struct net_device *dsa_dev_to_net_device(struct device *dev) |
91da11f87 net: Distributed ... |
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
{ 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 net: dsa: Rename ... |
154 |
EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); |
91da11f87 net: Distributed ... |
155 |
|
90af1059c net: dsa: forward... |
156 157 158 159 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 |
/* 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 net: dsa: reduce ... |
187 |
static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, |
89e49506b dsa: remove unuse... |
188 |
struct packet_type *pt, struct net_device *unused) |
3e8a72d1d net: dsa: reduce ... |
189 |
{ |
2f657a600 net: dsa: change ... |
190 |
struct dsa_port *cpu_dp = dev->dsa_ptr; |
a86d8becc net: dsa: Factor ... |
191 |
struct sk_buff *nskb = NULL; |
5f6b4e14c net: dsa: User pe... |
192 |
struct pcpu_sw_netstats *s; |
f613ed665 net: dsa: Add sup... |
193 |
struct dsa_slave_priv *p; |
3e8a72d1d net: dsa: reduce ... |
194 |
|
2f657a600 net: dsa: change ... |
195 |
if (unlikely(!cpu_dp)) { |
3e8a72d1d net: dsa: reduce ... |
196 197 198 |
kfree_skb(skb); return 0; } |
16c5dcb13 net: dsa: Move sk... |
199 200 201 |
skb = skb_unshare(skb, GFP_ATOMIC); if (!skb) return 0; |
2f657a600 net: dsa: change ... |
202 |
nskb = cpu_dp->rcv(skb, dev, pt); |
a86d8becc net: dsa: Factor ... |
203 204 205 206 207 208 |
if (!nskb) { kfree_skb(skb); return 0; } skb = nskb; |
f613ed665 net: dsa: Add sup... |
209 |
p = netdev_priv(skb->dev); |
a86d8becc net: dsa: Factor ... |
210 211 212 |
skb_push(skb, ETH_HLEN); skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); |
1dc0408cd net: dsa: Call ds... |
213 214 215 216 217 218 219 220 |
if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { nskb = dsa_untag_bridge_pvid(skb); if (!nskb) { kfree_skb(skb); return 0; } skb = nskb; } |
5f6b4e14c net: dsa: User pe... |
221 222 223 224 225 |
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 net: dsa: Factor ... |
226 |
|
90af1059c net: dsa: forward... |
227 228 |
if (dsa_skb_defer_rx_timestamp(p, skb)) return 0; |
e131a5634 net: dsa: add GRO... |
229 |
gro_cells_receive(&p->gcells, skb); |
a86d8becc net: dsa: Factor ... |
230 231 |
return 0; |
3e8a72d1d net: dsa: reduce ... |
232 |
} |
ac2629a47 net: dsa: Move ds... |
233 |
#ifdef CONFIG_PM_SLEEP |
e7d53ad32 net: dsa: unexpor... |
234 235 |
static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) { |
68bb8ea8a net: dsa: use dsa... |
236 237 238 |
const struct dsa_port *dp = dsa_to_port(ds, p); return dp->type == DSA_PORT_TYPE_USER && dp->slave; |
e7d53ad32 net: dsa: unexpor... |
239 |
} |
ac2629a47 net: dsa: Move ds... |
240 241 242 243 244 245 246 247 |
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; |
68bb8ea8a net: dsa: use dsa... |
248 |
ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave); |
ac2629a47 net: dsa: Move ds... |
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
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; |
68bb8ea8a net: dsa: use dsa... |
274 |
ret = dsa_slave_resume(dsa_to_port(ds, i)->slave); |
ac2629a47 net: dsa: Move ds... |
275 276 277 278 279 280 281 282 |
if (ret) return ret; } return 0; } EXPORT_SYMBOL_GPL(dsa_switch_resume); #endif |
61b7363ff net: dsa: make ds... |
283 |
static struct packet_type dsa_pack_type __read_mostly = { |
3e8a72d1d net: dsa: reduce ... |
284 285 286 |
.type = cpu_to_be16(ETH_P_XDSA), .func = dsa_switch_rcv, }; |
c9eb3e0f8 net: dsa: Add sup... |
287 288 289 290 291 292 |
static struct workqueue_struct *dsa_owq; bool dsa_schedule_work(struct work_struct *work) { return queue_work(dsa_owq, work); } |
60724d4ba net: dsa: Add sup... |
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
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); |
6b2975242 net: dsa: Add sup... |
314 315 316 |
int dsa_devlink_param_get(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx) { |
ccc3e6b01 net: dsa: Add hel... |
317 |
struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
6b2975242 net: dsa: Add sup... |
318 319 320 321 322 323 324 325 326 327 328 |
if (!ds->ops->devlink_param_get) return -EOPNOTSUPP; return ds->ops->devlink_param_get(ds, id, ctx); } EXPORT_SYMBOL_GPL(dsa_devlink_param_get); int dsa_devlink_param_set(struct devlink *dl, u32 id, struct devlink_param_gset_ctx *ctx) { |
ccc3e6b01 net: dsa: Add hel... |
329 |
struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
6b2975242 net: dsa: Add sup... |
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
if (!ds->ops->devlink_param_set) return -EOPNOTSUPP; return ds->ops->devlink_param_set(ds, id, ctx); } EXPORT_SYMBOL_GPL(dsa_devlink_param_set); int dsa_devlink_params_register(struct dsa_switch *ds, const struct devlink_param *params, size_t params_count) { return devlink_params_register(ds->devlink, params, params_count); } EXPORT_SYMBOL_GPL(dsa_devlink_params_register); void dsa_devlink_params_unregister(struct dsa_switch *ds, const struct devlink_param *params, size_t params_count) { devlink_params_unregister(ds->devlink, params, params_count); } EXPORT_SYMBOL_GPL(dsa_devlink_params_unregister); |
5cd73fbd7 net: dsa: Add sup... |
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
int dsa_devlink_resource_register(struct dsa_switch *ds, const char *resource_name, u64 resource_size, u64 resource_id, u64 parent_resource_id, const struct devlink_resource_size_params *size_params) { return devlink_resource_register(ds->devlink, resource_name, resource_size, resource_id, parent_resource_id, size_params); } EXPORT_SYMBOL_GPL(dsa_devlink_resource_register); void dsa_devlink_resources_unregister(struct dsa_switch *ds) { devlink_resources_unregister(ds->devlink, NULL); } EXPORT_SYMBOL_GPL(dsa_devlink_resources_unregister); void dsa_devlink_resource_occ_get_register(struct dsa_switch *ds, u64 resource_id, devlink_resource_occ_get_t *occ_get, void *occ_get_priv) { return devlink_resource_occ_get_register(ds->devlink, resource_id, occ_get, occ_get_priv); } EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_register); void dsa_devlink_resource_occ_get_unregister(struct dsa_switch *ds, u64 resource_id) { devlink_resource_occ_get_unregister(ds->devlink, resource_id); } EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_unregister); |
97c82c231 net: dsa: Add dev... |
389 390 391 392 393 394 395 396 397 |
struct devlink_region * dsa_devlink_region_create(struct dsa_switch *ds, const struct devlink_region_ops *ops, u32 region_max_snapshots, u64 region_size) { return devlink_region_create(ds->devlink, ops, region_max_snapshots, region_size); } EXPORT_SYMBOL_GPL(dsa_devlink_region_create); |
08156ba43 net: dsa: Add dev... |
398 399 400 401 402 403 404 405 406 407 408 409 410 |
struct devlink_region * dsa_devlink_port_region_create(struct dsa_switch *ds, int port, const struct devlink_port_region_ops *ops, u32 region_max_snapshots, u64 region_size) { struct dsa_port *dp = dsa_to_port(ds, port); return devlink_port_region_create(&dp->devlink_port, ops, region_max_snapshots, region_size); } EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create); |
97c82c231 net: dsa: Add dev... |
411 412 413 414 415 |
void dsa_devlink_region_destroy(struct devlink_region *region) { devlink_region_destroy(region); } EXPORT_SYMBOL_GPL(dsa_devlink_region_destroy); |
e1eea8112 net: dsa: introdu... |
416 417 418 419 420 421 422 423 |
struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) { if (!netdev || !dsa_slave_dev_check(netdev)) return ERR_PTR(-ENODEV); return dsa_slave_to_port(netdev); } EXPORT_SYMBOL_GPL(dsa_port_from_netdev); |
91da11f87 net: Distributed ... |
424 425 |
static int __init dsa_init_module(void) { |
7df899c36 dsa: Combine core... |
426 |
int rc; |
c9eb3e0f8 net: dsa: Add sup... |
427 428 429 430 |
dsa_owq = alloc_ordered_workqueue("dsa_ordered", WQ_MEM_RECLAIM); if (!dsa_owq) return -ENOMEM; |
88e4f0ca4 net: dsa: move ne... |
431 432 |
rc = dsa_slave_register_notifier(); if (rc) |
68be93024 net: dsa: Fix err... |
433 |
goto register_notifier_fail; |
b73adef67 net: dsa: integra... |
434 |
|
3e8a72d1d net: dsa: reduce ... |
435 |
dev_add_pack(&dsa_pack_type); |
409065b06 dsa: Register the... |
436 437 |
dsa_tag_driver_register(&DSA_TAG_DRIVER_NAME(none_ops), THIS_MODULE); |
7df899c36 dsa: Combine core... |
438 |
return 0; |
68be93024 net: dsa: Fix err... |
439 |
|
68be93024 net: dsa: Fix err... |
440 441 442 443 |
register_notifier_fail: destroy_workqueue(dsa_owq); return rc; |
91da11f87 net: Distributed ... |
444 445 446 447 448 |
} module_init(dsa_init_module); static void __exit dsa_cleanup_module(void) { |
409065b06 dsa: Register the... |
449 |
dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops)); |
88e4f0ca4 net: dsa: move ne... |
450 |
dsa_slave_unregister_notifier(); |
3e8a72d1d net: dsa: reduce ... |
451 |
dev_remove_pack(&dsa_pack_type); |
c9eb3e0f8 net: dsa: Add sup... |
452 |
destroy_workqueue(dsa_owq); |
91da11f87 net: Distributed ... |
453 454 |
} module_exit(dsa_cleanup_module); |
577d6a7c3 module: fix missi... |
455 |
MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); |
91da11f87 net: Distributed ... |
456 457 458 |
MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:dsa"); |