| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * net/dsa/dsa.c - Hardware switch handling |
|---|
| 3 | 4 | * Copyright (c) 2008-2009 Marvell Semiconductor |
|---|
| 4 | 5 | * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org> |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 8 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | | - * (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/device.h> |
|---|
| .. | .. |
|---|
| 27 | 23 | |
|---|
| 28 | 24 | #include "dsa_priv.h" |
|---|
| 29 | 25 | |
|---|
| 26 | +static LIST_HEAD(dsa_tag_drivers_list); |
|---|
| 27 | +static DEFINE_MUTEX(dsa_tag_drivers_lock); |
|---|
| 28 | + |
|---|
| 30 | 29 | static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb, |
|---|
| 31 | 30 | struct net_device *dev) |
|---|
| 32 | 31 | { |
|---|
| .. | .. |
|---|
| 35 | 34 | } |
|---|
| 36 | 35 | |
|---|
| 37 | 36 | static const struct dsa_device_ops none_ops = { |
|---|
| 37 | + .name = "none", |
|---|
| 38 | + .proto = DSA_TAG_PROTO_NONE, |
|---|
| 38 | 39 | .xmit = dsa_slave_notag_xmit, |
|---|
| 39 | 40 | .rcv = NULL, |
|---|
| 40 | 41 | }; |
|---|
| 41 | 42 | |
|---|
| 42 | | -const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { |
|---|
| 43 | | -#ifdef CONFIG_NET_DSA_TAG_BRCM |
|---|
| 44 | | - [DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops, |
|---|
| 45 | | -#endif |
|---|
| 46 | | -#ifdef CONFIG_NET_DSA_TAG_BRCM_PREPEND |
|---|
| 47 | | - [DSA_TAG_PROTO_BRCM_PREPEND] = &brcm_prepend_netdev_ops, |
|---|
| 48 | | -#endif |
|---|
| 49 | | -#ifdef CONFIG_NET_DSA_TAG_DSA |
|---|
| 50 | | - [DSA_TAG_PROTO_DSA] = &dsa_netdev_ops, |
|---|
| 51 | | -#endif |
|---|
| 52 | | -#ifdef CONFIG_NET_DSA_TAG_EDSA |
|---|
| 53 | | - [DSA_TAG_PROTO_EDSA] = &edsa_netdev_ops, |
|---|
| 54 | | -#endif |
|---|
| 55 | | -#ifdef CONFIG_NET_DSA_TAG_KSZ |
|---|
| 56 | | - [DSA_TAG_PROTO_KSZ] = &ksz_netdev_ops, |
|---|
| 57 | | -#endif |
|---|
| 58 | | -#ifdef CONFIG_NET_DSA_TAG_LAN9303 |
|---|
| 59 | | - [DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops, |
|---|
| 60 | | -#endif |
|---|
| 61 | | -#ifdef CONFIG_NET_DSA_TAG_MTK |
|---|
| 62 | | - [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops, |
|---|
| 63 | | -#endif |
|---|
| 64 | | -#ifdef CONFIG_NET_DSA_TAG_QCA |
|---|
| 65 | | - [DSA_TAG_PROTO_QCA] = &qca_netdev_ops, |
|---|
| 66 | | -#endif |
|---|
| 67 | | -#ifdef CONFIG_NET_DSA_TAG_TRAILER |
|---|
| 68 | | - [DSA_TAG_PROTO_TRAILER] = &trailer_netdev_ops, |
|---|
| 69 | | -#endif |
|---|
| 70 | | - [DSA_TAG_PROTO_NONE] = &none_ops, |
|---|
| 43 | +DSA_TAG_DRIVER(none_ops); |
|---|
| 44 | + |
|---|
| 45 | +static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver, |
|---|
| 46 | + struct module *owner) |
|---|
| 47 | +{ |
|---|
| 48 | + dsa_tag_driver->owner = owner; |
|---|
| 49 | + |
|---|
| 50 | + mutex_lock(&dsa_tag_drivers_lock); |
|---|
| 51 | + list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list); |
|---|
| 52 | + mutex_unlock(&dsa_tag_drivers_lock); |
|---|
| 53 | +} |
|---|
| 54 | + |
|---|
| 55 | +void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], |
|---|
| 56 | + unsigned int count, struct module *owner) |
|---|
| 57 | +{ |
|---|
| 58 | + unsigned int i; |
|---|
| 59 | + |
|---|
| 60 | + for (i = 0; i < count; i++) |
|---|
| 61 | + dsa_tag_driver_register(dsa_tag_driver_array[i], owner); |
|---|
| 62 | +} |
|---|
| 63 | + |
|---|
| 64 | +static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver) |
|---|
| 65 | +{ |
|---|
| 66 | + mutex_lock(&dsa_tag_drivers_lock); |
|---|
| 67 | + list_del(&dsa_tag_driver->list); |
|---|
| 68 | + mutex_unlock(&dsa_tag_drivers_lock); |
|---|
| 69 | +} |
|---|
| 70 | +EXPORT_SYMBOL_GPL(dsa_tag_drivers_register); |
|---|
| 71 | + |
|---|
| 72 | +void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], |
|---|
| 73 | + unsigned int count) |
|---|
| 74 | +{ |
|---|
| 75 | + unsigned int i; |
|---|
| 76 | + |
|---|
| 77 | + for (i = 0; i < count; i++) |
|---|
| 78 | + dsa_tag_driver_unregister(dsa_tag_driver_array[i]); |
|---|
| 79 | +} |
|---|
| 80 | +EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister); |
|---|
| 81 | + |
|---|
| 82 | +const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) |
|---|
| 83 | +{ |
|---|
| 84 | + return ops->name; |
|---|
| 71 | 85 | }; |
|---|
| 72 | 86 | |
|---|
| 73 | | -const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol) |
|---|
| 87 | +const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol) |
|---|
| 74 | 88 | { |
|---|
| 89 | + struct dsa_tag_driver *dsa_tag_driver; |
|---|
| 75 | 90 | const struct dsa_device_ops *ops; |
|---|
| 91 | + bool found = false; |
|---|
| 76 | 92 | |
|---|
| 77 | | - if (tag_protocol >= DSA_TAG_LAST) |
|---|
| 78 | | - return ERR_PTR(-EINVAL); |
|---|
| 79 | | - ops = dsa_device_ops[tag_protocol]; |
|---|
| 93 | + request_module("%s%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); |
|---|
| 80 | 94 | |
|---|
| 81 | | - if (!ops) |
|---|
| 82 | | - return ERR_PTR(-ENOPROTOOPT); |
|---|
| 95 | + mutex_lock(&dsa_tag_drivers_lock); |
|---|
| 96 | + list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { |
|---|
| 97 | + ops = dsa_tag_driver->ops; |
|---|
| 98 | + if (ops->proto == tag_protocol) { |
|---|
| 99 | + found = true; |
|---|
| 100 | + break; |
|---|
| 101 | + } |
|---|
| 102 | + } |
|---|
| 103 | + |
|---|
| 104 | + if (found) { |
|---|
| 105 | + if (!try_module_get(dsa_tag_driver->owner)) |
|---|
| 106 | + ops = ERR_PTR(-ENOPROTOOPT); |
|---|
| 107 | + } else { |
|---|
| 108 | + ops = ERR_PTR(-ENOPROTOOPT); |
|---|
| 109 | + } |
|---|
| 110 | + |
|---|
| 111 | + mutex_unlock(&dsa_tag_drivers_lock); |
|---|
| 83 | 112 | |
|---|
| 84 | 113 | return ops; |
|---|
| 114 | +} |
|---|
| 115 | + |
|---|
| 116 | +void dsa_tag_driver_put(const struct dsa_device_ops *ops) |
|---|
| 117 | +{ |
|---|
| 118 | + struct dsa_tag_driver *dsa_tag_driver; |
|---|
| 119 | + |
|---|
| 120 | + mutex_lock(&dsa_tag_drivers_lock); |
|---|
| 121 | + list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { |
|---|
| 122 | + if (dsa_tag_driver->ops == ops) { |
|---|
| 123 | + module_put(dsa_tag_driver->owner); |
|---|
| 124 | + break; |
|---|
| 125 | + } |
|---|
| 126 | + } |
|---|
| 127 | + mutex_unlock(&dsa_tag_drivers_lock); |
|---|
| 85 | 128 | } |
|---|
| 86 | 129 | |
|---|
| 87 | 130 | static int dev_is_class(struct device *dev, void *class) |
|---|
| .. | .. |
|---|
| 182 | 225 | skb->pkt_type = PACKET_HOST; |
|---|
| 183 | 226 | skb->protocol = eth_type_trans(skb, skb->dev); |
|---|
| 184 | 227 | |
|---|
| 228 | + if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { |
|---|
| 229 | + nskb = dsa_untag_bridge_pvid(skb); |
|---|
| 230 | + if (!nskb) { |
|---|
| 231 | + kfree_skb(skb); |
|---|
| 232 | + return 0; |
|---|
| 233 | + } |
|---|
| 234 | + skb = nskb; |
|---|
| 235 | + } |
|---|
| 236 | + |
|---|
| 185 | 237 | s = this_cpu_ptr(p->stats64); |
|---|
| 186 | 238 | u64_stats_update_begin(&s->syncp); |
|---|
| 187 | 239 | s->rx_packets++; |
|---|
| .. | .. |
|---|
| 199 | 251 | #ifdef CONFIG_PM_SLEEP |
|---|
| 200 | 252 | static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) |
|---|
| 201 | 253 | { |
|---|
| 202 | | - return dsa_is_user_port(ds, p) && ds->ports[p].slave; |
|---|
| 254 | + const struct dsa_port *dp = dsa_to_port(ds, p); |
|---|
| 255 | + |
|---|
| 256 | + return dp->type == DSA_PORT_TYPE_USER && dp->slave; |
|---|
| 203 | 257 | } |
|---|
| 204 | 258 | |
|---|
| 205 | 259 | int dsa_switch_suspend(struct dsa_switch *ds) |
|---|
| .. | .. |
|---|
| 211 | 265 | if (!dsa_is_port_initialized(ds, i)) |
|---|
| 212 | 266 | continue; |
|---|
| 213 | 267 | |
|---|
| 214 | | - ret = dsa_slave_suspend(ds->ports[i].slave); |
|---|
| 268 | + ret = dsa_slave_suspend(dsa_to_port(ds, i)->slave); |
|---|
| 215 | 269 | if (ret) |
|---|
| 216 | 270 | return ret; |
|---|
| 217 | 271 | } |
|---|
| .. | .. |
|---|
| 238 | 292 | if (!dsa_is_port_initialized(ds, i)) |
|---|
| 239 | 293 | continue; |
|---|
| 240 | 294 | |
|---|
| 241 | | - ret = dsa_slave_resume(ds->ports[i].slave); |
|---|
| 295 | + ret = dsa_slave_resume(dsa_to_port(ds, i)->slave); |
|---|
| 242 | 296 | if (ret) |
|---|
| 243 | 297 | return ret; |
|---|
| 244 | 298 | } |
|---|
| .. | .. |
|---|
| 282 | 336 | } |
|---|
| 283 | 337 | EXPORT_SYMBOL_GPL(call_dsa_notifiers); |
|---|
| 284 | 338 | |
|---|
| 339 | +int dsa_devlink_param_get(struct devlink *dl, u32 id, |
|---|
| 340 | + struct devlink_param_gset_ctx *ctx) |
|---|
| 341 | +{ |
|---|
| 342 | + struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
|---|
| 343 | + |
|---|
| 344 | + if (!ds->ops->devlink_param_get) |
|---|
| 345 | + return -EOPNOTSUPP; |
|---|
| 346 | + |
|---|
| 347 | + return ds->ops->devlink_param_get(ds, id, ctx); |
|---|
| 348 | +} |
|---|
| 349 | +EXPORT_SYMBOL_GPL(dsa_devlink_param_get); |
|---|
| 350 | + |
|---|
| 351 | +int dsa_devlink_param_set(struct devlink *dl, u32 id, |
|---|
| 352 | + struct devlink_param_gset_ctx *ctx) |
|---|
| 353 | +{ |
|---|
| 354 | + struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
|---|
| 355 | + |
|---|
| 356 | + if (!ds->ops->devlink_param_set) |
|---|
| 357 | + return -EOPNOTSUPP; |
|---|
| 358 | + |
|---|
| 359 | + return ds->ops->devlink_param_set(ds, id, ctx); |
|---|
| 360 | +} |
|---|
| 361 | +EXPORT_SYMBOL_GPL(dsa_devlink_param_set); |
|---|
| 362 | + |
|---|
| 363 | +int dsa_devlink_params_register(struct dsa_switch *ds, |
|---|
| 364 | + const struct devlink_param *params, |
|---|
| 365 | + size_t params_count) |
|---|
| 366 | +{ |
|---|
| 367 | + return devlink_params_register(ds->devlink, params, params_count); |
|---|
| 368 | +} |
|---|
| 369 | +EXPORT_SYMBOL_GPL(dsa_devlink_params_register); |
|---|
| 370 | + |
|---|
| 371 | +void dsa_devlink_params_unregister(struct dsa_switch *ds, |
|---|
| 372 | + const struct devlink_param *params, |
|---|
| 373 | + size_t params_count) |
|---|
| 374 | +{ |
|---|
| 375 | + devlink_params_unregister(ds->devlink, params, params_count); |
|---|
| 376 | +} |
|---|
| 377 | +EXPORT_SYMBOL_GPL(dsa_devlink_params_unregister); |
|---|
| 378 | + |
|---|
| 379 | +int dsa_devlink_resource_register(struct dsa_switch *ds, |
|---|
| 380 | + const char *resource_name, |
|---|
| 381 | + u64 resource_size, |
|---|
| 382 | + u64 resource_id, |
|---|
| 383 | + u64 parent_resource_id, |
|---|
| 384 | + const struct devlink_resource_size_params *size_params) |
|---|
| 385 | +{ |
|---|
| 386 | + return devlink_resource_register(ds->devlink, resource_name, |
|---|
| 387 | + resource_size, resource_id, |
|---|
| 388 | + parent_resource_id, |
|---|
| 389 | + size_params); |
|---|
| 390 | +} |
|---|
| 391 | +EXPORT_SYMBOL_GPL(dsa_devlink_resource_register); |
|---|
| 392 | + |
|---|
| 393 | +void dsa_devlink_resources_unregister(struct dsa_switch *ds) |
|---|
| 394 | +{ |
|---|
| 395 | + devlink_resources_unregister(ds->devlink, NULL); |
|---|
| 396 | +} |
|---|
| 397 | +EXPORT_SYMBOL_GPL(dsa_devlink_resources_unregister); |
|---|
| 398 | + |
|---|
| 399 | +void dsa_devlink_resource_occ_get_register(struct dsa_switch *ds, |
|---|
| 400 | + u64 resource_id, |
|---|
| 401 | + devlink_resource_occ_get_t *occ_get, |
|---|
| 402 | + void *occ_get_priv) |
|---|
| 403 | +{ |
|---|
| 404 | + return devlink_resource_occ_get_register(ds->devlink, resource_id, |
|---|
| 405 | + occ_get, occ_get_priv); |
|---|
| 406 | +} |
|---|
| 407 | +EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_register); |
|---|
| 408 | + |
|---|
| 409 | +void dsa_devlink_resource_occ_get_unregister(struct dsa_switch *ds, |
|---|
| 410 | + u64 resource_id) |
|---|
| 411 | +{ |
|---|
| 412 | + devlink_resource_occ_get_unregister(ds->devlink, resource_id); |
|---|
| 413 | +} |
|---|
| 414 | +EXPORT_SYMBOL_GPL(dsa_devlink_resource_occ_get_unregister); |
|---|
| 415 | + |
|---|
| 416 | +struct devlink_region * |
|---|
| 417 | +dsa_devlink_region_create(struct dsa_switch *ds, |
|---|
| 418 | + const struct devlink_region_ops *ops, |
|---|
| 419 | + u32 region_max_snapshots, u64 region_size) |
|---|
| 420 | +{ |
|---|
| 421 | + return devlink_region_create(ds->devlink, ops, region_max_snapshots, |
|---|
| 422 | + region_size); |
|---|
| 423 | +} |
|---|
| 424 | +EXPORT_SYMBOL_GPL(dsa_devlink_region_create); |
|---|
| 425 | + |
|---|
| 426 | +struct devlink_region * |
|---|
| 427 | +dsa_devlink_port_region_create(struct dsa_switch *ds, |
|---|
| 428 | + int port, |
|---|
| 429 | + const struct devlink_port_region_ops *ops, |
|---|
| 430 | + u32 region_max_snapshots, u64 region_size) |
|---|
| 431 | +{ |
|---|
| 432 | + struct dsa_port *dp = dsa_to_port(ds, port); |
|---|
| 433 | + |
|---|
| 434 | + return devlink_port_region_create(&dp->devlink_port, ops, |
|---|
| 435 | + region_max_snapshots, |
|---|
| 436 | + region_size); |
|---|
| 437 | +} |
|---|
| 438 | +EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create); |
|---|
| 439 | + |
|---|
| 440 | +void dsa_devlink_region_destroy(struct devlink_region *region) |
|---|
| 441 | +{ |
|---|
| 442 | + devlink_region_destroy(region); |
|---|
| 443 | +} |
|---|
| 444 | +EXPORT_SYMBOL_GPL(dsa_devlink_region_destroy); |
|---|
| 445 | + |
|---|
| 446 | +struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) |
|---|
| 447 | +{ |
|---|
| 448 | + if (!netdev || !dsa_slave_dev_check(netdev)) |
|---|
| 449 | + return ERR_PTR(-ENODEV); |
|---|
| 450 | + |
|---|
| 451 | + return dsa_slave_to_port(netdev); |
|---|
| 452 | +} |
|---|
| 453 | +EXPORT_SYMBOL_GPL(dsa_port_from_netdev); |
|---|
| 454 | + |
|---|
| 285 | 455 | static int __init dsa_init_module(void) |
|---|
| 286 | 456 | { |
|---|
| 287 | 457 | int rc; |
|---|
| .. | .. |
|---|
| 295 | 465 | if (rc) |
|---|
| 296 | 466 | goto register_notifier_fail; |
|---|
| 297 | 467 | |
|---|
| 298 | | - rc = dsa_legacy_register(); |
|---|
| 299 | | - if (rc) |
|---|
| 300 | | - goto legacy_register_fail; |
|---|
| 301 | | - |
|---|
| 302 | 468 | dev_add_pack(&dsa_pack_type); |
|---|
| 469 | + |
|---|
| 470 | + dsa_tag_driver_register(&DSA_TAG_DRIVER_NAME(none_ops), |
|---|
| 471 | + THIS_MODULE); |
|---|
| 303 | 472 | |
|---|
| 304 | 473 | return 0; |
|---|
| 305 | 474 | |
|---|
| 306 | | -legacy_register_fail: |
|---|
| 307 | | - dsa_slave_unregister_notifier(); |
|---|
| 308 | 475 | register_notifier_fail: |
|---|
| 309 | 476 | destroy_workqueue(dsa_owq); |
|---|
| 310 | 477 | |
|---|
| .. | .. |
|---|
| 314 | 481 | |
|---|
| 315 | 482 | static void __exit dsa_cleanup_module(void) |
|---|
| 316 | 483 | { |
|---|
| 484 | + dsa_tag_driver_unregister(&DSA_TAG_DRIVER_NAME(none_ops)); |
|---|
| 485 | + |
|---|
| 317 | 486 | dsa_slave_unregister_notifier(); |
|---|
| 318 | 487 | dev_remove_pack(&dsa_pack_type); |
|---|
| 319 | | - dsa_legacy_unregister(); |
|---|
| 320 | 488 | destroy_workqueue(dsa_owq); |
|---|
| 321 | 489 | } |
|---|
| 322 | 490 | module_exit(dsa_cleanup_module); |
|---|