2019-05-27 06:55:01 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2016-06-04 19:17:07 +00:00
|
|
|
/*
|
|
|
|
* net/dsa/dsa2.c - Hardware switch handling, binding version 2
|
|
|
|
* Copyright (c) 2008-2009 Marvell Semiconductor
|
|
|
|
* Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
|
|
|
|
* Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/list.h>
|
2017-03-28 21:45:06 +00:00
|
|
|
#include <linux/netdevice.h>
|
2016-06-04 19:17:07 +00:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_net.h>
|
2019-03-24 10:14:26 +00:00
|
|
|
#include <net/devlink.h>
|
2017-05-17 19:46:03 +00:00
|
|
|
|
2016-06-04 19:17:07 +00:00
|
|
|
#include "dsa_priv.h"
|
|
|
|
|
|
|
|
static DEFINE_MUTEX(dsa2_mutex);
|
net: dsa: implement auto-normalization of MTU for bridge hardware datapath
Many switches don't have an explicit knob for configuring the MTU
(maximum transmission unit per interface). Instead, they do the
length-based packet admission checks on the ingress interface, for
reasons that are easy to understand (why would you accept a packet in
the queuing subsystem if you know you're going to drop it anyway).
So it is actually the MRU that these switches permit configuring.
In Linux there only exists the IFLA_MTU netlink attribute and the
associated dev_set_mtu function. The comments like to play blind and say
that it's changing the "maximum transfer unit", which is to say that
there isn't any directionality in the meaning of the MTU word. So that
is the interpretation that this patch is giving to things: MTU == MRU.
When 2 interfaces having different MTUs are bridged, the bridge driver
MTU auto-adjustment logic kicks in: what br_mtu_auto_adjust() does is it
adjusts the MTU of the bridge net device itself (and not that of the
slave net devices) to the minimum value of all slave interfaces, in
order for forwarded packets to not exceed the MTU regardless of the
interface they are received and send on.
The idea behind this behavior, and why the slave MTUs are not adjusted,
is that normal termination from Linux over the L2 forwarding domain
should happen over the bridge net device, which _is_ properly limited by
the minimum MTU. And termination over individual slave devices is
possible even if those are bridged. But that is not "forwarding", so
there's no reason to do normalization there, since only a single
interface sees that packet.
The problem with those switches that can only control the MRU is with
the offloaded data path, where a packet received on an interface with
MRU 9000 would still be forwarded to an interface with MRU 1500. And the
br_mtu_auto_adjust() function does not really help, since the MTU
configured on the bridge net device is ignored.
In order to enforce the de-facto MTU == MRU rule for these switches, we
need to do MTU normalization, which means: in order for no packet larger
than the MTU configured on this port to be sent, then we need to limit
the MRU on all ports that this packet could possibly come from. AKA
since we are configuring the MRU via MTU, it means that all ports within
a bridge forwarding domain should have the same MTU.
And that is exactly what this patch is trying to do.
>From an implementation perspective, we try to follow the intent of the
user, otherwise there is a risk that we might livelock them (they try to
change the MTU on an already-bridged interface, but we just keep
changing it back in an attempt to keep the MTU normalized). So the MTU
that the bridge is normalized to is either:
- The most recently changed one:
ip link set dev swp0 master br0
ip link set dev swp1 master br0
ip link set dev swp0 mtu 1400
This sequence will make swp1 inherit MTU 1400 from swp0.
- The one of the most recently added interface to the bridge:
ip link set dev swp0 master br0
ip link set dev swp1 mtu 1400
ip link set dev swp1 master br0
The above sequence will make swp0 inherit MTU 1400 as well.
Suggested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-03-27 19:55:43 +00:00
|
|
|
LIST_HEAD(dsa_tree_list);
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2021-01-29 01:00:04 +00:00
|
|
|
/**
|
|
|
|
* dsa_tree_notify - Execute code for all switches in a DSA switch tree.
|
|
|
|
* @dst: collection of struct dsa_switch devices to notify.
|
|
|
|
* @e: event, must be of type DSA_NOTIFIER_*
|
|
|
|
* @v: event-specific value.
|
|
|
|
*
|
|
|
|
* Given a struct dsa_switch_tree, this can be used to run a function once for
|
|
|
|
* each member DSA switch. The other alternative of traversing the tree is only
|
|
|
|
* through its ports list, which does not uniquely list the switches.
|
|
|
|
*/
|
|
|
|
int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v)
|
|
|
|
{
|
|
|
|
struct raw_notifier_head *nh = &dst->nh;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = raw_notifier_call_chain(nh, e, v);
|
|
|
|
|
|
|
|
return notifier_to_errno(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dsa_broadcast - Notify all DSA trees in the system.
|
|
|
|
* @e: event, must be of type DSA_NOTIFIER_*
|
|
|
|
* @v: event-specific value.
|
|
|
|
*
|
|
|
|
* Can be used to notify the switching fabric of events such as cross-chip
|
|
|
|
* bridging between disjoint trees (such as islands of tagger-compatible
|
|
|
|
* switches bridged by an incompatible middle switch).
|
|
|
|
*/
|
|
|
|
int dsa_broadcast(unsigned long e, void *v)
|
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(dst, &dsa_tree_list, list) {
|
|
|
|
err = dsa_tree_notify(dst, e, v);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-01-13 08:42:53 +00:00
|
|
|
/**
|
|
|
|
* dsa_lag_map() - Map LAG netdev to a linear LAG ID
|
|
|
|
* @dst: Tree in which to record the mapping.
|
|
|
|
* @lag: Netdev that is to be mapped to an ID.
|
|
|
|
*
|
|
|
|
* dsa_lag_id/dsa_lag_dev can then be used to translate between the
|
|
|
|
* two spaces. The size of the mapping space is determined by the
|
|
|
|
* driver by setting ds->num_lag_ids. It is perfectly legal to leave
|
|
|
|
* it unset if it is not needed, in which case these functions become
|
|
|
|
* no-ops.
|
|
|
|
*/
|
|
|
|
void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag)
|
|
|
|
{
|
|
|
|
unsigned int id;
|
|
|
|
|
|
|
|
if (dsa_lag_id(dst, lag) >= 0)
|
|
|
|
/* Already mapped */
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (id = 0; id < dst->lags_len; id++) {
|
|
|
|
if (!dsa_lag_dev(dst, id)) {
|
|
|
|
dst->lags[id] = lag;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No IDs left, which is OK. Some drivers do not need it. The
|
|
|
|
* ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id
|
|
|
|
* returns an error for this device when joining the LAG. The
|
|
|
|
* driver can then return -EOPNOTSUPP back to DSA, which will
|
|
|
|
* fall back to a software LAG.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dsa_lag_unmap() - Remove a LAG ID mapping
|
|
|
|
* @dst: Tree in which the mapping is recorded.
|
|
|
|
* @lag: Netdev that was mapped.
|
|
|
|
*
|
|
|
|
* As there may be multiple users of the mapping, it is only removed
|
|
|
|
* if there are no other references to it.
|
|
|
|
*/
|
|
|
|
void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag)
|
|
|
|
{
|
|
|
|
struct dsa_port *dp;
|
|
|
|
unsigned int id;
|
|
|
|
|
|
|
|
dsa_lag_foreach_port(dp, dst, lag)
|
|
|
|
/* There are remaining users of this mapping */
|
|
|
|
return;
|
|
|
|
|
|
|
|
dsa_lags_foreach_id(id, dst) {
|
|
|
|
if (dsa_lag_dev(dst, id) == lag) {
|
|
|
|
dst->lags[id] = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 16:37:42 +00:00
|
|
|
struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
|
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
struct dsa_port *dp;
|
|
|
|
|
|
|
|
list_for_each_entry(dst, &dsa_tree_list, list) {
|
|
|
|
if (dst->index != tree_index)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
|
|
|
if (dp->ds->index != sw_index)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return dp->ds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dsa_switch_find);
|
|
|
|
|
2017-11-03 23:05:24 +00:00
|
|
|
static struct dsa_switch_tree *dsa_tree_find(int index)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
|
2017-11-03 23:05:24 +00:00
|
|
|
list_for_each_entry(dst, &dsa_tree_list, list)
|
2017-11-03 23:05:22 +00:00
|
|
|
if (dst->index == index)
|
2016-06-04 19:17:07 +00:00
|
|
|
return dst;
|
2017-11-03 23:05:22 +00:00
|
|
|
|
2016-06-04 19:17:07 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:24 +00:00
|
|
|
static struct dsa_switch_tree *dsa_tree_alloc(int index)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
|
|
|
|
dst = kzalloc(sizeof(*dst), GFP_KERNEL);
|
|
|
|
if (!dst)
|
|
|
|
return NULL;
|
2017-11-03 23:05:24 +00:00
|
|
|
|
2017-11-03 23:05:21 +00:00
|
|
|
dst->index = index;
|
2017-11-03 23:05:24 +00:00
|
|
|
|
2019-10-31 02:09:13 +00:00
|
|
|
INIT_LIST_HEAD(&dst->rtable);
|
|
|
|
|
2019-10-21 20:51:16 +00:00
|
|
|
INIT_LIST_HEAD(&dst->ports);
|
|
|
|
|
2016-06-04 19:17:07 +00:00
|
|
|
INIT_LIST_HEAD(&dst->list);
|
2019-10-18 21:02:46 +00:00
|
|
|
list_add_tail(&dst->list, &dsa_tree_list);
|
2017-11-03 23:05:22 +00:00
|
|
|
|
2016-06-04 19:17:07 +00:00
|
|
|
kref_init(&dst->refcount);
|
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:23 +00:00
|
|
|
static void dsa_tree_free(struct dsa_switch_tree *dst)
|
|
|
|
{
|
net: dsa: keep a copy of the tagging protocol in the DSA switch tree
Cascading DSA switches can be done multiple ways. There is the brute
force approach / tag stacking, where one upstream switch, located
between leaf switches and the host Ethernet controller, will just
happily transport the DSA header of those leaf switches as payload.
For this kind of setups, DSA works without any special kind of treatment
compared to a single switch - they just aren't aware of each other.
Then there's the approach where the upstream switch understands the tags
it transports from its leaves below, as it doesn't push a tag of its own,
but it routes based on the source port & switch id information present
in that tag (as opposed to DMAC & VID) and it strips the tag when
egressing a front-facing port. Currently only Marvell implements the
latter, and Marvell DSA trees contain only Marvell switches.
So it is safe to say that DSA trees already have a single tag protocol
shared by all switches, and in fact this is what makes the switches able
to understand each other. This fact is also implied by the fact that
currently, the tagging protocol is reported as part of a sysfs installed
on the DSA master and not per port, so it must be the same for all the
ports connected to that DSA master regardless of the switch that they
belong to.
It's time to make this official and enforce it (yes, this also means we
won't have any "switch understands tag to some extent but is not able to
speak it" hardware oddities that we'll support in the future).
This is needed due to the imminent introduction of the dsa_switch_ops::
change_tag_protocol driver API. When that is introduced, we'll have
to notify switches of the tagging protocol that they're configured to
use. Currently the tag_ops structure pointer is held only for CPU ports.
But there are switches which don't have CPU ports and nonetheless still
need to be configured. These would be Marvell leaf switches whose
upstream port is just a DSA link. How do we inform these of their
tagging protocol setup/deletion?
One answer to the above would be: iterate through the DSA switch tree's
ports once, list the CPU ports, get their tag_ops, then iterate again
now that we have it, and notify everybody of that tag_ops. But what to
do if conflicts appear between one cpu_dp->tag_ops and another? There's
no escaping the fact that conflict resolution needs to be done, so we
can be upfront about it.
Ease our work and just keep the master copy of the tag_ops inside the
struct dsa_switch_tree. Reference counting is now moved to be per-tree
too, instead of per-CPU port.
There are many places in the data path that access master->dsa_ptr->tag_ops
and we would introduce unnecessary performance penalty going through yet
another indirection, so keep those right where they are.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-01-29 01:00:05 +00:00
|
|
|
if (dst->tag_ops)
|
|
|
|
dsa_tag_driver_put(dst->tag_ops);
|
2017-11-03 23:05:23 +00:00
|
|
|
list_del(&dst->list);
|
|
|
|
kfree(dst);
|
|
|
|
}
|
|
|
|
|
2017-11-24 16:36:06 +00:00
|
|
|
static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst)
|
2017-11-03 23:05:24 +00:00
|
|
|
{
|
2017-11-24 16:36:06 +00:00
|
|
|
if (dst)
|
|
|
|
kref_get(&dst->refcount);
|
2017-11-03 23:05:24 +00:00
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
2017-11-24 16:36:06 +00:00
|
|
|
static struct dsa_switch_tree *dsa_tree_touch(int index)
|
2017-11-03 23:05:23 +00:00
|
|
|
{
|
2017-11-24 16:36:06 +00:00
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
|
|
|
|
dst = dsa_tree_find(index);
|
|
|
|
if (dst)
|
|
|
|
return dsa_tree_get(dst);
|
|
|
|
else
|
|
|
|
return dsa_tree_alloc(index);
|
2017-11-03 23:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_tree_release(struct kref *ref)
|
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
|
|
|
|
dst = container_of(ref, struct dsa_switch_tree, refcount);
|
|
|
|
|
|
|
|
dsa_tree_free(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_tree_put(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2017-11-24 16:36:06 +00:00
|
|
|
if (dst)
|
|
|
|
kref_put(&dst->refcount, dsa_tree_release);
|
2017-11-03 23:05:23 +00:00
|
|
|
}
|
|
|
|
|
2017-01-26 18:45:52 +00:00
|
|
|
static bool dsa_port_is_dsa(struct dsa_port *port)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2017-10-27 19:55:15 +00:00
|
|
|
return port->type == DSA_PORT_TYPE_DSA;
|
2017-01-26 18:45:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool dsa_port_is_cpu(struct dsa_port *port)
|
|
|
|
{
|
2017-10-27 19:55:15 +00:00
|
|
|
return port->type == DSA_PORT_TYPE_CPU;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:44 +00:00
|
|
|
static bool dsa_port_is_user(struct dsa_port *dp)
|
|
|
|
{
|
|
|
|
return dp->type == DSA_PORT_TYPE_USER;
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:49 +00:00
|
|
|
static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
|
|
|
|
struct device_node *dn)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2017-11-06 21:11:49 +00:00
|
|
|
struct dsa_port *dp;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-10-21 20:51:21 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
if (dp->dn == dn)
|
|
|
|
return dp;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-17 11:20:38 +00:00
|
|
|
static struct dsa_link *dsa_link_touch(struct dsa_port *dp,
|
|
|
|
struct dsa_port *link_dp)
|
2019-10-31 02:09:13 +00:00
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dp->ds;
|
|
|
|
struct dsa_switch_tree *dst;
|
|
|
|
struct dsa_link *dl;
|
|
|
|
|
|
|
|
dst = ds->dst;
|
|
|
|
|
|
|
|
list_for_each_entry(dl, &dst->rtable, list)
|
|
|
|
if (dl->dp == dp && dl->link_dp == link_dp)
|
|
|
|
return dl;
|
|
|
|
|
|
|
|
dl = kzalloc(sizeof(*dl), GFP_KERNEL);
|
|
|
|
if (!dl)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
dl->dp = dp;
|
|
|
|
dl->link_dp = link_dp;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&dl->list);
|
|
|
|
list_add_tail(&dl->list, &dst->rtable);
|
|
|
|
|
|
|
|
return dl;
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:51 +00:00
|
|
|
static bool dsa_port_setup_routing_table(struct dsa_port *dp)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2017-11-06 21:11:51 +00:00
|
|
|
struct dsa_switch *ds = dp->ds;
|
|
|
|
struct dsa_switch_tree *dst = ds->dst;
|
|
|
|
struct device_node *dn = dp->dn;
|
2017-11-06 21:11:50 +00:00
|
|
|
struct of_phandle_iterator it;
|
2017-11-06 21:11:49 +00:00
|
|
|
struct dsa_port *link_dp;
|
2019-10-31 02:09:13 +00:00
|
|
|
struct dsa_link *dl;
|
2017-11-06 21:11:50 +00:00
|
|
|
int err;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2017-11-06 21:11:50 +00:00
|
|
|
of_for_each_phandle(&it, err, dn, "link", NULL, 0) {
|
|
|
|
link_dp = dsa_tree_find_port_by_node(dst, it.node);
|
|
|
|
if (!link_dp) {
|
|
|
|
of_node_put(it.node);
|
2017-11-06 21:11:51 +00:00
|
|
|
return false;
|
2017-11-06 21:11:50 +00:00
|
|
|
}
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-10-31 02:09:13 +00:00
|
|
|
dl = dsa_link_touch(dp, link_dp);
|
|
|
|
if (!dl) {
|
|
|
|
of_node_put(it.node);
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:51 +00:00
|
|
|
return true;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2019-10-31 02:09:15 +00:00
|
|
|
static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2017-11-06 21:11:51 +00:00
|
|
|
bool complete = true;
|
|
|
|
struct dsa_port *dp;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-10-21 20:51:20 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
2019-10-31 02:09:15 +00:00
|
|
|
if (dsa_port_is_dsa(dp)) {
|
2017-11-06 21:11:51 +00:00
|
|
|
complete = dsa_port_setup_routing_table(dp);
|
|
|
|
if (!complete)
|
|
|
|
break;
|
|
|
|
}
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:51 +00:00
|
|
|
return complete;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:44 +00:00
|
|
|
static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst)
|
|
|
|
{
|
|
|
|
struct dsa_port *dp;
|
|
|
|
|
2019-10-21 20:51:23 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
if (dsa_port_is_cpu(dp))
|
|
|
|
return dp;
|
2017-11-06 21:11:44 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2019-10-21 20:51:24 +00:00
|
|
|
struct dsa_port *cpu_dp, *dp;
|
2017-11-06 21:11:44 +00:00
|
|
|
|
2019-10-21 20:51:24 +00:00
|
|
|
cpu_dp = dsa_tree_find_first_cpu(dst);
|
|
|
|
if (!cpu_dp) {
|
|
|
|
pr_err("DSA: tree %d has no CPU port\n", dst->index);
|
2017-11-06 21:11:44 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assign the default CPU port to all ports of the fabric */
|
2019-10-21 20:51:24 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
|
|
|
|
dp->cpu_dp = cpu_dp;
|
2017-11-06 21:11:44 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2019-10-21 20:51:24 +00:00
|
|
|
struct dsa_port *dp;
|
|
|
|
|
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
|
|
|
|
dp->cpu_dp = NULL;
|
2017-11-06 21:11:44 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:48 +00:00
|
|
|
static int dsa_port_setup(struct dsa_port *dp)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2019-08-19 20:00:48 +00:00
|
|
|
struct devlink_port *dlp = &dp->devlink_port;
|
2019-08-31 12:46:19 +00:00
|
|
|
bool dsa_port_link_registered = false;
|
|
|
|
bool dsa_port_enabled = false;
|
|
|
|
int err = 0;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
if (dp->setup)
|
|
|
|
return 0;
|
|
|
|
|
2017-11-06 21:11:48 +00:00
|
|
|
switch (dp->type) {
|
|
|
|
case DSA_PORT_TYPE_UNUSED:
|
2019-08-19 20:00:50 +00:00
|
|
|
dsa_port_disable(dp);
|
2017-11-06 21:11:48 +00:00
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_CPU:
|
2018-05-18 07:29:03 +00:00
|
|
|
err = dsa_port_link_register_of(dp);
|
2019-08-19 20:00:50 +00:00
|
|
|
if (err)
|
2019-08-31 12:46:19 +00:00
|
|
|
break;
|
|
|
|
dsa_port_link_registered = true;
|
2019-08-19 20:00:50 +00:00
|
|
|
|
|
|
|
err = dsa_port_enable(dp, NULL);
|
2019-05-30 06:09:07 +00:00
|
|
|
if (err)
|
2019-08-31 12:46:19 +00:00
|
|
|
break;
|
|
|
|
dsa_port_enabled = true;
|
|
|
|
|
2018-05-18 07:29:03 +00:00
|
|
|
break;
|
2017-11-06 21:11:48 +00:00
|
|
|
case DSA_PORT_TYPE_DSA:
|
2018-01-23 15:03:46 +00:00
|
|
|
err = dsa_port_link_register_of(dp);
|
2019-08-19 20:00:50 +00:00
|
|
|
if (err)
|
2019-08-31 12:46:19 +00:00
|
|
|
break;
|
|
|
|
dsa_port_link_registered = true;
|
2019-08-19 20:00:50 +00:00
|
|
|
|
|
|
|
err = dsa_port_enable(dp, NULL);
|
2019-05-30 06:09:07 +00:00
|
|
|
if (err)
|
2019-08-31 12:46:19 +00:00
|
|
|
break;
|
|
|
|
dsa_port_enabled = true;
|
|
|
|
|
2017-11-06 21:11:48 +00:00
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_USER:
|
2019-08-19 20:00:48 +00:00
|
|
|
dp->mac = of_get_mac_address(dp->dn);
|
2017-11-06 21:11:48 +00:00
|
|
|
err = dsa_slave_create(dp);
|
|
|
|
if (err)
|
2019-08-31 12:46:19 +00:00
|
|
|
break;
|
2019-08-19 20:00:48 +00:00
|
|
|
|
|
|
|
devlink_port_type_eth_set(dlp, dp->slave);
|
2017-11-06 21:11:48 +00:00
|
|
|
break;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2019-08-31 12:46:19 +00:00
|
|
|
if (err && dsa_port_enabled)
|
|
|
|
dsa_port_disable(dp);
|
|
|
|
if (err && dsa_port_link_registered)
|
|
|
|
dsa_port_link_unregister_of(dp);
|
2019-10-21 20:51:19 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
2019-08-31 12:46:19 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
dp->setup = true;
|
|
|
|
|
|
|
|
return 0;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 16:12:53 +00:00
|
|
|
static int dsa_port_devlink_setup(struct dsa_port *dp)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2019-08-19 20:00:48 +00:00
|
|
|
struct devlink_port *dlp = &dp->devlink_port;
|
2020-10-04 16:12:53 +00:00
|
|
|
struct dsa_switch_tree *dst = dp->ds->dst;
|
|
|
|
struct devlink_port_attrs attrs = {};
|
|
|
|
struct devlink *dl = dp->ds->devlink;
|
|
|
|
const unsigned char *id;
|
|
|
|
unsigned char len;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
id = (const unsigned char *)&dst->index;
|
|
|
|
len = sizeof(dst->index);
|
|
|
|
|
|
|
|
attrs.phys.port_number = dp->index;
|
|
|
|
memcpy(attrs.switch_id.id, id, len);
|
|
|
|
attrs.switch_id.id_len = len;
|
|
|
|
memset(dlp, 0, sizeof(*dlp));
|
|
|
|
|
|
|
|
switch (dp->type) {
|
|
|
|
case DSA_PORT_TYPE_UNUSED:
|
|
|
|
attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
|
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_CPU:
|
|
|
|
attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
|
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_DSA:
|
|
|
|
attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
|
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_USER:
|
|
|
|
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
devlink_port_attrs_set(dlp, &attrs);
|
|
|
|
err = devlink_port_register(dl, dlp, dp->index);
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
dp->devlink_port_setup = true;
|
2017-11-06 21:11:48 +00:00
|
|
|
|
2020-10-04 16:12:53 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_port_teardown(struct dsa_port *dp)
|
|
|
|
{
|
2021-01-12 00:48:31 +00:00
|
|
|
struct devlink_port *dlp = &dp->devlink_port;
|
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
if (!dp->setup)
|
|
|
|
return;
|
|
|
|
|
2021-01-12 00:48:31 +00:00
|
|
|
devlink_port_type_clear(dlp);
|
|
|
|
|
2017-11-06 21:11:48 +00:00
|
|
|
switch (dp->type) {
|
|
|
|
case DSA_PORT_TYPE_UNUSED:
|
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_CPU:
|
2019-08-19 20:00:50 +00:00
|
|
|
dsa_port_disable(dp);
|
2019-08-19 20:00:48 +00:00
|
|
|
dsa_port_link_unregister_of(dp);
|
|
|
|
break;
|
2017-11-06 21:11:48 +00:00
|
|
|
case DSA_PORT_TYPE_DSA:
|
2019-08-19 20:00:50 +00:00
|
|
|
dsa_port_disable(dp);
|
2018-01-23 15:03:46 +00:00
|
|
|
dsa_port_link_unregister_of(dp);
|
2017-11-06 21:11:48 +00:00
|
|
|
break;
|
|
|
|
case DSA_PORT_TYPE_USER:
|
|
|
|
if (dp->slave) {
|
|
|
|
dsa_slave_destroy(dp->slave);
|
|
|
|
dp->slave = NULL;
|
|
|
|
}
|
|
|
|
break;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
2019-10-21 20:51:19 +00:00
|
|
|
|
|
|
|
dp->setup = false;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-04 16:12:53 +00:00
|
|
|
static void dsa_port_devlink_teardown(struct dsa_port *dp)
|
|
|
|
{
|
|
|
|
struct devlink_port *dlp = &dp->devlink_port;
|
|
|
|
|
|
|
|
if (dp->devlink_port_setup)
|
|
|
|
devlink_port_unregister(dlp);
|
|
|
|
dp->devlink_port_setup = false;
|
|
|
|
}
|
|
|
|
|
2020-09-18 19:11:08 +00:00
|
|
|
static int dsa_devlink_info_get(struct devlink *dl,
|
|
|
|
struct devlink_info_req *req,
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_to_ds(dl);
|
|
|
|
|
|
|
|
if (ds->ops->devlink_info_get)
|
|
|
|
return ds->ops->devlink_info_get(ds, req, extack);
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2021-01-15 02:11:13 +00:00
|
|
|
static int dsa_devlink_sb_pool_get(struct devlink *dl,
|
|
|
|
unsigned int sb_index, u16 pool_index,
|
|
|
|
struct devlink_sb_pool_info *pool_info)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_to_ds(dl);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_pool_get)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_pool_get(ds, sb_index, pool_index,
|
|
|
|
pool_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index,
|
|
|
|
u16 pool_index, u32 size,
|
|
|
|
enum devlink_sb_threshold_type threshold_type,
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_to_ds(dl);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_pool_set)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_pool_set(ds, sb_index, pool_index, size,
|
|
|
|
threshold_type, extack);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_devlink_sb_port_pool_get(struct devlink_port *dlp,
|
|
|
|
unsigned int sb_index, u16 pool_index,
|
|
|
|
u32 *p_threshold)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
|
|
|
|
int port = dsa_devlink_port_to_port(dlp);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_port_pool_get)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_port_pool_get(ds, port, sb_index,
|
|
|
|
pool_index, p_threshold);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_devlink_sb_port_pool_set(struct devlink_port *dlp,
|
|
|
|
unsigned int sb_index, u16 pool_index,
|
|
|
|
u32 threshold,
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
|
|
|
|
int port = dsa_devlink_port_to_port(dlp);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_port_pool_set)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_port_pool_set(ds, port, sb_index,
|
|
|
|
pool_index, threshold, extack);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dsa_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp,
|
|
|
|
unsigned int sb_index, u16 tc_index,
|
|
|
|
enum devlink_sb_pool_type pool_type,
|
|
|
|
u16 *p_pool_index, u32 *p_threshold)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
|
|
|
|
int port = dsa_devlink_port_to_port(dlp);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_tc_pool_bind_get)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_tc_pool_bind_get(ds, port, sb_index,
|
|
|
|
tc_index, pool_type,
|
|
|
|
p_pool_index, p_threshold);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dsa_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp,
|
|
|
|
unsigned int sb_index, u16 tc_index,
|
|
|
|
enum devlink_sb_pool_type pool_type,
|
|
|
|
u16 pool_index, u32 threshold,
|
|
|
|
struct netlink_ext_ack *extack)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
|
|
|
|
int port = dsa_devlink_port_to_port(dlp);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_tc_pool_bind_set)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_tc_pool_bind_set(ds, port, sb_index,
|
|
|
|
tc_index, pool_type,
|
|
|
|
pool_index, threshold,
|
|
|
|
extack);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_devlink_sb_occ_snapshot(struct devlink *dl,
|
|
|
|
unsigned int sb_index)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_to_ds(dl);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_occ_snapshot)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_occ_snapshot(ds, sb_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_devlink_sb_occ_max_clear(struct devlink *dl,
|
|
|
|
unsigned int sb_index)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_to_ds(dl);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_occ_max_clear)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_occ_max_clear(ds, sb_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_devlink_sb_occ_port_pool_get(struct devlink_port *dlp,
|
|
|
|
unsigned int sb_index,
|
|
|
|
u16 pool_index, u32 *p_cur,
|
|
|
|
u32 *p_max)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
|
|
|
|
int port = dsa_devlink_port_to_port(dlp);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_occ_port_pool_get)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_occ_port_pool_get(ds, port, sb_index,
|
|
|
|
pool_index, p_cur, p_max);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp,
|
|
|
|
unsigned int sb_index, u16 tc_index,
|
|
|
|
enum devlink_sb_pool_type pool_type,
|
|
|
|
u32 *p_cur, u32 *p_max)
|
|
|
|
{
|
|
|
|
struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
|
|
|
|
int port = dsa_devlink_port_to_port(dlp);
|
|
|
|
|
|
|
|
if (!ds->ops->devlink_sb_occ_tc_port_bind_get)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return ds->ops->devlink_sb_occ_tc_port_bind_get(ds, port,
|
|
|
|
sb_index, tc_index,
|
|
|
|
pool_type, p_cur,
|
|
|
|
p_max);
|
|
|
|
}
|
|
|
|
|
2020-09-18 19:11:08 +00:00
|
|
|
static const struct devlink_ops dsa_devlink_ops = {
|
2021-01-15 02:11:13 +00:00
|
|
|
.info_get = dsa_devlink_info_get,
|
|
|
|
.sb_pool_get = dsa_devlink_sb_pool_get,
|
|
|
|
.sb_pool_set = dsa_devlink_sb_pool_set,
|
|
|
|
.sb_port_pool_get = dsa_devlink_sb_port_pool_get,
|
|
|
|
.sb_port_pool_set = dsa_devlink_sb_port_pool_set,
|
|
|
|
.sb_tc_pool_bind_get = dsa_devlink_sb_tc_pool_bind_get,
|
|
|
|
.sb_tc_pool_bind_set = dsa_devlink_sb_tc_pool_bind_set,
|
|
|
|
.sb_occ_snapshot = dsa_devlink_sb_occ_snapshot,
|
|
|
|
.sb_occ_max_clear = dsa_devlink_sb_occ_max_clear,
|
|
|
|
.sb_occ_port_pool_get = dsa_devlink_sb_occ_port_pool_get,
|
|
|
|
.sb_occ_tc_port_bind_get = dsa_devlink_sb_occ_tc_port_bind_get,
|
2020-09-18 19:11:08 +00:00
|
|
|
};
|
|
|
|
|
2017-11-06 21:11:47 +00:00
|
|
|
static int dsa_switch_setup(struct dsa_switch *ds)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2019-10-24 23:03:51 +00:00
|
|
|
struct dsa_devlink_priv *dl_priv;
|
2020-10-04 16:12:53 +00:00
|
|
|
struct dsa_port *dp;
|
2019-10-21 20:51:19 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (ds->setup)
|
|
|
|
return 0;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2016-06-07 23:32:39 +00:00
|
|
|
/* Initialize ds->phys_mii_mask before registering the slave MDIO bus
|
2016-08-23 16:38:56 +00:00
|
|
|
* driver and before ops->setup() has run, since the switch drivers and
|
2016-06-07 23:32:39 +00:00
|
|
|
* the slave MDIO bus driver rely on these values for probing PHY
|
|
|
|
* devices or not
|
|
|
|
*/
|
2017-10-26 15:22:56 +00:00
|
|
|
ds->phys_mii_mask |= dsa_user_ports(ds);
|
2016-06-07 23:32:39 +00:00
|
|
|
|
2017-03-28 21:45:07 +00:00
|
|
|
/* Add the switch to devlink before calling setup, so that setup can
|
|
|
|
* add dpipe tables
|
|
|
|
*/
|
2019-10-24 23:03:51 +00:00
|
|
|
ds->devlink = devlink_alloc(&dsa_devlink_ops, sizeof(*dl_priv));
|
2017-03-28 21:45:07 +00:00
|
|
|
if (!ds->devlink)
|
|
|
|
return -ENOMEM;
|
2019-10-24 23:03:51 +00:00
|
|
|
dl_priv = devlink_priv(ds->devlink);
|
|
|
|
dl_priv->ds = ds;
|
2017-03-28 21:45:07 +00:00
|
|
|
|
|
|
|
err = devlink_register(ds->devlink, ds->dev);
|
|
|
|
if (err)
|
2019-05-30 06:09:07 +00:00
|
|
|
goto free_devlink;
|
2017-03-28 21:45:07 +00:00
|
|
|
|
2020-10-04 16:12:53 +00:00
|
|
|
/* Setup devlink port instances now, so that the switch
|
|
|
|
* setup() can register regions etc, against the ports
|
|
|
|
*/
|
|
|
|
list_for_each_entry(dp, &ds->dst->ports, list) {
|
|
|
|
if (dp->ds == ds) {
|
|
|
|
err = dsa_port_devlink_setup(dp);
|
|
|
|
if (err)
|
|
|
|
goto unregister_devlink_ports;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-03 18:20:20 +00:00
|
|
|
err = dsa_switch_register_notifier(ds);
|
|
|
|
if (err)
|
2020-10-04 16:12:53 +00:00
|
|
|
goto unregister_devlink_ports;
|
2017-02-03 18:20:20 +00:00
|
|
|
|
net: dsa: set configure_vlan_while_not_filtering to true by default
As explained in commit 54a0ed0df496 ("net: dsa: provide an option for
drivers to always receive bridge VLANs"), DSA has historically been
skipping VLAN switchdev operations when the bridge wasn't in
vlan_filtering mode, but the reason why it was doing that has never been
clear. So the configure_vlan_while_not_filtering option is there merely
to preserve functionality for existing drivers. It isn't some behavior
that drivers should opt into. Ideally, when all drivers leave this flag
set, we can delete the dsa_port_skip_vlan_configuration() function.
New drivers always seem to omit setting this flag, for some reason. So
let's reverse the logic: the DSA core sets it by default to true before
the .setup() callback, and legacy drivers can turn it off. This way, new
drivers get the new behavior by default, unless they explicitly set the
flag to false, which is more obvious during review.
Remove the assignment from drivers which were setting it to true, and
add the assignment to false for the drivers that didn't previously have
it. This way, it should be easier to see how many we have left.
The following drivers: lan9303, mv88e6060 were skipped from setting this
flag to false, because they didn't have any VLAN offload ops in the
first place.
The Broadcom Starfighter 2 driver calls the common b53_switch_alloc and
therefore also inherits the configure_vlan_while_not_filtering=true
behavior.
Also, print a message through netlink extack every time a VLAN has been
skipped. This is mildly annoying on purpose, so that (a) it is at least
clear that VLANs are being skipped - the legacy behavior in itself is
confusing, and the extack should be much more difficult to miss, unlike
kernel logs - and (b) people have one more incentive to convert to the
new behavior.
No behavior change except for the added prints is intended at this time.
$ ip link add br0 type bridge vlan_filtering 0
$ ip link set sw0p2 master br0
[ 60.315148] br0: port 1(sw0p2) entered blocking state
[ 60.320350] br0: port 1(sw0p2) entered disabled state
[ 60.327839] device sw0p2 entered promiscuous mode
[ 60.334905] br0: port 1(sw0p2) entered blocking state
[ 60.340142] br0: port 1(sw0p2) entered forwarding state
Warning: dsa_core: skipping configuration of VLAN. # This was the pvid
$ bridge vlan add dev sw0p2 vid 100
Warning: dsa_core: skipping configuration of VLAN.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Link: https://lore.kernel.org/r/20210115231919.43834-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-01-15 23:19:19 +00:00
|
|
|
ds->configure_vlan_while_not_filtering = true;
|
|
|
|
|
2019-05-05 10:19:20 +00:00
|
|
|
err = ds->ops->setup(ds);
|
|
|
|
if (err < 0)
|
2019-05-30 06:09:07 +00:00
|
|
|
goto unregister_notifier;
|
2019-05-05 10:19:20 +00:00
|
|
|
|
2019-10-24 23:03:51 +00:00
|
|
|
devlink_params_publish(ds->devlink);
|
|
|
|
|
2016-08-23 16:38:56 +00:00
|
|
|
if (!ds->slave_mii_bus && ds->ops->phy_read) {
|
2016-06-07 23:32:40 +00:00
|
|
|
ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
|
2019-05-30 06:09:07 +00:00
|
|
|
if (!ds->slave_mii_bus) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto unregister_notifier;
|
|
|
|
}
|
2016-06-07 23:32:40 +00:00
|
|
|
|
|
|
|
dsa_slave_mii_bus_init(ds);
|
|
|
|
|
|
|
|
err = mdiobus_register(ds->slave_mii_bus);
|
|
|
|
if (err < 0)
|
2019-05-30 06:09:07 +00:00
|
|
|
goto unregister_notifier;
|
2016-06-07 23:32:40 +00:00
|
|
|
}
|
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
ds->setup = true;
|
|
|
|
|
2016-06-04 19:17:07 +00:00
|
|
|
return 0;
|
2019-05-30 06:09:07 +00:00
|
|
|
|
|
|
|
unregister_notifier:
|
|
|
|
dsa_switch_unregister_notifier(ds);
|
2020-10-04 16:12:53 +00:00
|
|
|
unregister_devlink_ports:
|
|
|
|
list_for_each_entry(dp, &ds->dst->ports, list)
|
|
|
|
if (dp->ds == ds)
|
|
|
|
dsa_port_devlink_teardown(dp);
|
2019-05-30 06:09:07 +00:00
|
|
|
devlink_unregister(ds->devlink);
|
|
|
|
free_devlink:
|
|
|
|
devlink_free(ds->devlink);
|
|
|
|
ds->devlink = NULL;
|
|
|
|
|
|
|
|
return err;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:47 +00:00
|
|
|
static void dsa_switch_teardown(struct dsa_switch *ds)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2020-10-04 16:12:53 +00:00
|
|
|
struct dsa_port *dp;
|
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
if (!ds->setup)
|
|
|
|
return;
|
|
|
|
|
2016-08-23 16:38:56 +00:00
|
|
|
if (ds->slave_mii_bus && ds->ops->phy_read)
|
2016-06-07 23:32:40 +00:00
|
|
|
mdiobus_unregister(ds->slave_mii_bus);
|
2017-02-03 18:20:20 +00:00
|
|
|
|
|
|
|
dsa_switch_unregister_notifier(ds);
|
2017-03-28 21:45:07 +00:00
|
|
|
|
2019-06-08 12:04:28 +00:00
|
|
|
if (ds->ops->teardown)
|
|
|
|
ds->ops->teardown(ds);
|
|
|
|
|
2017-03-28 21:45:07 +00:00
|
|
|
if (ds->devlink) {
|
2020-10-04 16:12:53 +00:00
|
|
|
list_for_each_entry(dp, &ds->dst->ports, list)
|
|
|
|
if (dp->ds == ds)
|
|
|
|
dsa_port_devlink_teardown(dp);
|
2017-03-28 21:45:07 +00:00
|
|
|
devlink_unregister(ds->devlink);
|
|
|
|
devlink_free(ds->devlink);
|
|
|
|
ds->devlink = NULL;
|
|
|
|
}
|
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
ds->setup = false;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:47 +00:00
|
|
|
static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2017-11-06 21:11:48 +00:00
|
|
|
struct dsa_port *dp;
|
2019-10-21 20:51:19 +00:00
|
|
|
int err;
|
2017-11-06 21:11:47 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
|
|
|
err = dsa_switch_setup(dp->ds);
|
2017-11-06 21:11:47 +00:00
|
|
|
if (err)
|
2019-10-21 20:51:19 +00:00
|
|
|
goto teardown;
|
|
|
|
}
|
2017-11-06 21:11:48 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
|
|
|
err = dsa_port_setup(dp);
|
|
|
|
if (err)
|
2020-05-04 03:50:57 +00:00
|
|
|
continue;
|
2017-11-06 21:11:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2019-05-30 06:09:07 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
teardown:
|
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
dsa_port_teardown(dp);
|
2019-05-30 06:09:07 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
dsa_switch_teardown(dp->ds);
|
2019-05-30 06:09:07 +00:00
|
|
|
|
|
|
|
return err;
|
2017-11-06 21:11:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2017-11-06 21:11:48 +00:00
|
|
|
struct dsa_port *dp;
|
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
dsa_port_teardown(dp);
|
2017-11-06 21:11:48 +00:00
|
|
|
|
2019-10-21 20:51:19 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
dsa_switch_teardown(dp->ds);
|
2017-11-06 21:11:47 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:45 +00:00
|
|
|
static int dsa_tree_setup_master(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2019-10-21 20:51:22 +00:00
|
|
|
struct dsa_port *dp;
|
|
|
|
int err;
|
2017-11-06 21:11:45 +00:00
|
|
|
|
2019-10-21 20:51:22 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
|
|
|
if (dsa_port_is_cpu(dp)) {
|
|
|
|
err = dsa_master_setup(dp->master, dp);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2017-11-06 21:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
|
|
|
|
{
|
2019-10-21 20:51:22 +00:00
|
|
|
struct dsa_port *dp;
|
2017-11-06 21:11:45 +00:00
|
|
|
|
2019-10-21 20:51:22 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
if (dsa_port_is_cpu(dp))
|
|
|
|
dsa_master_teardown(dp->master);
|
2017-11-06 21:11:45 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 08:42:53 +00:00
|
|
|
static int dsa_tree_setup_lags(struct dsa_switch_tree *dst)
|
|
|
|
{
|
|
|
|
unsigned int len = 0;
|
|
|
|
struct dsa_port *dp;
|
|
|
|
|
|
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
|
|
|
if (dp->ds->num_lag_ids > len)
|
|
|
|
len = dp->ds->num_lag_ids;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL);
|
|
|
|
if (!dst->lags)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
dst->lags_len = len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst)
|
|
|
|
{
|
|
|
|
kfree(dst->lags);
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:46 +00:00
|
|
|
static int dsa_tree_setup(struct dsa_switch_tree *dst)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2017-11-06 21:11:51 +00:00
|
|
|
bool complete;
|
2016-06-04 19:17:07 +00:00
|
|
|
int err;
|
|
|
|
|
2017-11-06 21:11:46 +00:00
|
|
|
if (dst->setup) {
|
|
|
|
pr_err("DSA: tree %d already setup! Disjoint trees?\n",
|
|
|
|
dst->index);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:51 +00:00
|
|
|
complete = dsa_tree_setup_routing_table(dst);
|
|
|
|
if (!complete)
|
|
|
|
return 0;
|
|
|
|
|
2017-11-06 21:11:44 +00:00
|
|
|
err = dsa_tree_setup_default_cpu(dst);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-11-06 21:11:47 +00:00
|
|
|
err = dsa_tree_setup_switches(dst);
|
|
|
|
if (err)
|
2019-05-30 06:09:07 +00:00
|
|
|
goto teardown_default_cpu;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2017-11-06 21:11:45 +00:00
|
|
|
err = dsa_tree_setup_master(dst);
|
2017-09-19 15:56:59 +00:00
|
|
|
if (err)
|
2019-05-30 06:09:07 +00:00
|
|
|
goto teardown_switches;
|
2017-09-19 15:56:59 +00:00
|
|
|
|
2021-01-13 08:42:53 +00:00
|
|
|
err = dsa_tree_setup_lags(dst);
|
|
|
|
if (err)
|
|
|
|
goto teardown_master;
|
|
|
|
|
2017-11-06 21:11:46 +00:00
|
|
|
dst->setup = true;
|
|
|
|
|
|
|
|
pr_info("DSA: tree %d setup\n", dst->index);
|
2016-06-04 19:17:07 +00:00
|
|
|
|
|
|
|
return 0;
|
2019-05-30 06:09:07 +00:00
|
|
|
|
2021-01-13 08:42:53 +00:00
|
|
|
teardown_master:
|
|
|
|
dsa_tree_teardown_master(dst);
|
2019-05-30 06:09:07 +00:00
|
|
|
teardown_switches:
|
|
|
|
dsa_tree_teardown_switches(dst);
|
|
|
|
teardown_default_cpu:
|
|
|
|
dsa_tree_teardown_default_cpu(dst);
|
|
|
|
|
|
|
|
return err;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:46 +00:00
|
|
|
static void dsa_tree_teardown(struct dsa_switch_tree *dst)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2019-10-31 02:09:13 +00:00
|
|
|
struct dsa_link *dl, *next;
|
|
|
|
|
2017-11-06 21:11:46 +00:00
|
|
|
if (!dst->setup)
|
2016-06-04 19:17:07 +00:00
|
|
|
return;
|
|
|
|
|
2021-01-13 08:42:53 +00:00
|
|
|
dsa_tree_teardown_lags(dst);
|
|
|
|
|
2017-11-06 21:11:45 +00:00
|
|
|
dsa_tree_teardown_master(dst);
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2017-11-06 21:11:47 +00:00
|
|
|
dsa_tree_teardown_switches(dst);
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2017-11-06 21:11:44 +00:00
|
|
|
dsa_tree_teardown_default_cpu(dst);
|
2016-06-07 23:32:42 +00:00
|
|
|
|
2019-10-31 02:09:13 +00:00
|
|
|
list_for_each_entry_safe(dl, next, &dst->rtable, list) {
|
|
|
|
list_del(&dl->list);
|
|
|
|
kfree(dl);
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:46 +00:00
|
|
|
pr_info("DSA: tree %d torn down\n", dst->index);
|
|
|
|
|
|
|
|
dst->setup = false;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2019-10-21 20:51:16 +00:00
|
|
|
static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
|
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst = ds->dst;
|
|
|
|
struct dsa_port *dp;
|
|
|
|
|
2019-10-21 20:51:29 +00:00
|
|
|
list_for_each_entry(dp, &dst->ports, list)
|
|
|
|
if (dp->ds == ds && dp->index == index)
|
|
|
|
return dp;
|
|
|
|
|
|
|
|
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
|
|
|
|
if (!dp)
|
|
|
|
return NULL;
|
2019-10-21 20:51:16 +00:00
|
|
|
|
|
|
|
dp->ds = ds;
|
|
|
|
dp->index = index;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&dp->list);
|
|
|
|
list_add_tail(&dp->list, &dst->ports);
|
|
|
|
|
|
|
|
return dp;
|
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
static int dsa_port_parse_user(struct dsa_port *dp, const char *name)
|
|
|
|
{
|
|
|
|
if (!name)
|
|
|
|
name = "eth%d";
|
|
|
|
|
|
|
|
dp->type = DSA_PORT_TYPE_USER;
|
|
|
|
dp->name = name;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_port_parse_dsa(struct dsa_port *dp)
|
|
|
|
{
|
|
|
|
dp->type = DSA_PORT_TYPE_DSA;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-08 05:06:05 +00:00
|
|
|
static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp,
|
|
|
|
struct net_device *master)
|
|
|
|
{
|
|
|
|
enum dsa_tag_protocol tag_protocol = DSA_TAG_PROTO_NONE;
|
|
|
|
struct dsa_switch *mds, *ds = dp->ds;
|
|
|
|
unsigned int mdp_upstream;
|
|
|
|
struct dsa_port *mdp;
|
|
|
|
|
|
|
|
/* It is possible to stack DSA switches onto one another when that
|
|
|
|
* happens the switch driver may want to know if its tagging protocol
|
|
|
|
* is going to work in such a configuration.
|
|
|
|
*/
|
|
|
|
if (dsa_slave_dev_check(master)) {
|
|
|
|
mdp = dsa_slave_to_port(master);
|
|
|
|
mds = mdp->ds;
|
|
|
|
mdp_upstream = dsa_upstream_port(mds, mdp->index);
|
|
|
|
tag_protocol = mds->ops->get_tag_protocol(mds, mdp_upstream,
|
|
|
|
DSA_TAG_PROTO_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the master device is not itself a DSA slave in a disjoint DSA
|
|
|
|
* tree, then return immediately.
|
|
|
|
*/
|
|
|
|
return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol);
|
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
|
|
|
|
{
|
2017-11-03 23:05:30 +00:00
|
|
|
struct dsa_switch *ds = dp->ds;
|
|
|
|
struct dsa_switch_tree *dst = ds->dst;
|
|
|
|
enum dsa_tag_protocol tag_protocol;
|
|
|
|
|
2020-01-08 05:06:05 +00:00
|
|
|
tag_protocol = dsa_get_tag_protocol(dp, master);
|
net: dsa: keep a copy of the tagging protocol in the DSA switch tree
Cascading DSA switches can be done multiple ways. There is the brute
force approach / tag stacking, where one upstream switch, located
between leaf switches and the host Ethernet controller, will just
happily transport the DSA header of those leaf switches as payload.
For this kind of setups, DSA works without any special kind of treatment
compared to a single switch - they just aren't aware of each other.
Then there's the approach where the upstream switch understands the tags
it transports from its leaves below, as it doesn't push a tag of its own,
but it routes based on the source port & switch id information present
in that tag (as opposed to DMAC & VID) and it strips the tag when
egressing a front-facing port. Currently only Marvell implements the
latter, and Marvell DSA trees contain only Marvell switches.
So it is safe to say that DSA trees already have a single tag protocol
shared by all switches, and in fact this is what makes the switches able
to understand each other. This fact is also implied by the fact that
currently, the tagging protocol is reported as part of a sysfs installed
on the DSA master and not per port, so it must be the same for all the
ports connected to that DSA master regardless of the switch that they
belong to.
It's time to make this official and enforce it (yes, this also means we
won't have any "switch understands tag to some extent but is not able to
speak it" hardware oddities that we'll support in the future).
This is needed due to the imminent introduction of the dsa_switch_ops::
change_tag_protocol driver API. When that is introduced, we'll have
to notify switches of the tagging protocol that they're configured to
use. Currently the tag_ops structure pointer is held only for CPU ports.
But there are switches which don't have CPU ports and nonetheless still
need to be configured. These would be Marvell leaf switches whose
upstream port is just a DSA link. How do we inform these of their
tagging protocol setup/deletion?
One answer to the above would be: iterate through the DSA switch tree's
ports once, list the CPU ports, get their tag_ops, then iterate again
now that we have it, and notify everybody of that tag_ops. But what to
do if conflicts appear between one cpu_dp->tag_ops and another? There's
no escaping the fact that conflict resolution needs to be done, so we
can be upfront about it.
Ease our work and just keep the master copy of the tag_ops inside the
struct dsa_switch_tree. Reference counting is now moved to be per-tree
too, instead of per-CPU port.
There are many places in the data path that access master->dsa_ptr->tag_ops
and we would introduce unnecessary performance penalty going through yet
another indirection, so keep those right where they are.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-01-29 01:00:05 +00:00
|
|
|
if (dst->tag_ops) {
|
|
|
|
if (dst->tag_ops->proto != tag_protocol) {
|
|
|
|
dev_err(ds->dev,
|
|
|
|
"A DSA switch tree can have only one tagging protocol\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* In the case of multiple CPU ports per switch, the tagging
|
|
|
|
* protocol is still reference-counted only per switch tree, so
|
|
|
|
* nothing to do here.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
dst->tag_ops = dsa_tag_driver_get(tag_protocol);
|
|
|
|
if (IS_ERR(dst->tag_ops)) {
|
|
|
|
if (PTR_ERR(dst->tag_ops) == -ENOPROTOOPT)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
dev_warn(ds->dev, "No tagger for this switch\n");
|
|
|
|
dp->master = NULL;
|
|
|
|
return PTR_ERR(dst->tag_ops);
|
|
|
|
}
|
2017-11-03 23:05:30 +00:00
|
|
|
}
|
|
|
|
|
2020-01-08 05:06:05 +00:00
|
|
|
dp->master = master;
|
2017-11-03 23:05:29 +00:00
|
|
|
dp->type = DSA_PORT_TYPE_CPU;
|
net: dsa: keep a copy of the tagging protocol in the DSA switch tree
Cascading DSA switches can be done multiple ways. There is the brute
force approach / tag stacking, where one upstream switch, located
between leaf switches and the host Ethernet controller, will just
happily transport the DSA header of those leaf switches as payload.
For this kind of setups, DSA works without any special kind of treatment
compared to a single switch - they just aren't aware of each other.
Then there's the approach where the upstream switch understands the tags
it transports from its leaves below, as it doesn't push a tag of its own,
but it routes based on the source port & switch id information present
in that tag (as opposed to DMAC & VID) and it strips the tag when
egressing a front-facing port. Currently only Marvell implements the
latter, and Marvell DSA trees contain only Marvell switches.
So it is safe to say that DSA trees already have a single tag protocol
shared by all switches, and in fact this is what makes the switches able
to understand each other. This fact is also implied by the fact that
currently, the tagging protocol is reported as part of a sysfs installed
on the DSA master and not per port, so it must be the same for all the
ports connected to that DSA master regardless of the switch that they
belong to.
It's time to make this official and enforce it (yes, this also means we
won't have any "switch understands tag to some extent but is not able to
speak it" hardware oddities that we'll support in the future).
This is needed due to the imminent introduction of the dsa_switch_ops::
change_tag_protocol driver API. When that is introduced, we'll have
to notify switches of the tagging protocol that they're configured to
use. Currently the tag_ops structure pointer is held only for CPU ports.
But there are switches which don't have CPU ports and nonetheless still
need to be configured. These would be Marvell leaf switches whose
upstream port is just a DSA link. How do we inform these of their
tagging protocol setup/deletion?
One answer to the above would be: iterate through the DSA switch tree's
ports once, list the CPU ports, get their tag_ops, then iterate again
now that we have it, and notify everybody of that tag_ops. But what to
do if conflicts appear between one cpu_dp->tag_ops and another? There's
no escaping the fact that conflict resolution needs to be done, so we
can be upfront about it.
Ease our work and just keep the master copy of the tag_ops inside the
struct dsa_switch_tree. Reference counting is now moved to be per-tree
too, instead of per-CPU port.
There are many places in the data path that access master->dsa_ptr->tag_ops
and we would introduce unnecessary performance penalty going through yet
another indirection, so keep those right where they are.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-01-29 01:00:05 +00:00
|
|
|
dp->filter = dst->tag_ops->filter;
|
|
|
|
dp->rcv = dst->tag_ops->rcv;
|
|
|
|
dp->tag_ops = dst->tag_ops;
|
2017-11-03 23:05:30 +00:00
|
|
|
dp->dst = dst;
|
2017-11-03 23:05:29 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-27 19:55:14 +00:00
|
|
|
static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
|
|
|
|
{
|
2017-10-27 19:55:15 +00:00
|
|
|
struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
|
2017-10-27 19:55:18 +00:00
|
|
|
const char *name = of_get_property(dn, "label", NULL);
|
2017-11-03 23:05:28 +00:00
|
|
|
bool link = of_property_read_bool(dn, "link");
|
2017-10-27 19:55:15 +00:00
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
dp->dn = dn;
|
|
|
|
|
2017-10-27 19:55:15 +00:00
|
|
|
if (ethernet) {
|
2017-10-27 19:55:17 +00:00
|
|
|
struct net_device *master;
|
|
|
|
|
|
|
|
master = of_find_net_device_by_node(ethernet);
|
|
|
|
if (!master)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
return dsa_port_parse_cpu(dp, master);
|
2017-10-27 19:55:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
if (link)
|
|
|
|
return dsa_port_parse_dsa(dp);
|
2017-10-27 19:55:14 +00:00
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
return dsa_port_parse_user(dp, name);
|
2017-10-27 19:55:14 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
|
|
|
|
struct device_node *dn)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2017-10-27 19:55:13 +00:00
|
|
|
struct device_node *ports, *port;
|
2017-10-27 19:55:14 +00:00
|
|
|
struct dsa_port *dp;
|
2019-02-25 07:22:19 +00:00
|
|
|
int err = 0;
|
2016-06-04 19:17:07 +00:00
|
|
|
u32 reg;
|
2017-10-27 19:55:13 +00:00
|
|
|
|
|
|
|
ports = of_get_child_by_name(dn, "ports");
|
|
|
|
if (!ports) {
|
2020-07-20 12:49:39 +00:00
|
|
|
/* The second possibility is "ethernet-ports" */
|
|
|
|
ports = of_get_child_by_name(dn, "ethernet-ports");
|
|
|
|
if (!ports) {
|
|
|
|
dev_err(ds->dev, "no ports child node found\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-10-27 19:55:13 +00:00
|
|
|
}
|
2016-06-04 19:17:07 +00:00
|
|
|
|
|
|
|
for_each_available_child_of_node(ports, port) {
|
|
|
|
err = of_property_read_u32(port, "reg", ®);
|
|
|
|
if (err)
|
2019-02-25 07:22:19 +00:00
|
|
|
goto out_put_node;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-02-25 07:22:19 +00:00
|
|
|
if (reg >= ds->num_ports) {
|
2021-01-06 09:09:15 +00:00
|
|
|
dev_err(ds->dev, "port %pOF index %u exceeds num_ports (%zu)\n",
|
|
|
|
port, reg, ds->num_ports);
|
2019-02-25 07:22:19 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_put_node;
|
|
|
|
}
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-10-21 20:51:15 +00:00
|
|
|
dp = dsa_to_port(ds, reg);
|
2017-10-27 19:55:14 +00:00
|
|
|
|
|
|
|
err = dsa_port_parse_of(dp, port);
|
|
|
|
if (err)
|
2019-02-25 07:22:19 +00:00
|
|
|
goto out_put_node;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 07:22:19 +00:00
|
|
|
out_put_node:
|
|
|
|
of_node_put(ports);
|
|
|
|
return err;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
static int dsa_switch_parse_member_of(struct dsa_switch *ds,
|
|
|
|
struct device_node *dn)
|
|
|
|
{
|
|
|
|
u32 m[2] = { 0, 0 };
|
|
|
|
int sz;
|
|
|
|
|
|
|
|
/* Don't error out if this optional property isn't found */
|
|
|
|
sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2);
|
|
|
|
if (sz < 0 && sz != -EINVAL)
|
|
|
|
return sz;
|
|
|
|
|
|
|
|
ds->index = m[1];
|
|
|
|
|
|
|
|
ds->dst = dsa_tree_touch(m[0]);
|
|
|
|
if (!ds->dst)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-21 20:51:16 +00:00
|
|
|
static int dsa_switch_touch_ports(struct dsa_switch *ds)
|
|
|
|
{
|
|
|
|
struct dsa_port *dp;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
for (port = 0; port < ds->num_ports; port++) {
|
|
|
|
dp = dsa_port_touch(ds, port);
|
|
|
|
if (!dp)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = dsa_switch_parse_member_of(ds, dn);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2019-10-21 20:51:16 +00:00
|
|
|
err = dsa_switch_touch_ports(ds);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
return dsa_switch_parse_ports_of(ds, dn);
|
|
|
|
}
|
|
|
|
|
2017-10-27 19:55:14 +00:00
|
|
|
static int dsa_port_parse(struct dsa_port *dp, const char *name,
|
|
|
|
struct device *dev)
|
|
|
|
{
|
2017-10-27 19:55:15 +00:00
|
|
|
if (!strcmp(name, "cpu")) {
|
2017-10-27 19:55:17 +00:00
|
|
|
struct net_device *master;
|
|
|
|
|
|
|
|
master = dsa_dev_to_net_device(dev);
|
|
|
|
if (!master)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
|
|
|
|
dev_put(master);
|
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
return dsa_port_parse_cpu(dp, master);
|
2017-10-27 19:55:15 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
if (!strcmp(name, "dsa"))
|
|
|
|
return dsa_port_parse_dsa(dp);
|
2017-10-27 19:55:14 +00:00
|
|
|
|
2017-11-03 23:05:29 +00:00
|
|
|
return dsa_port_parse_user(dp, name);
|
2017-10-27 19:55:14 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
static int dsa_switch_parse_ports(struct dsa_switch *ds,
|
|
|
|
struct dsa_chip_data *cd)
|
2017-02-04 21:02:43 +00:00
|
|
|
{
|
|
|
|
bool valid_name_found = false;
|
2017-10-27 19:55:14 +00:00
|
|
|
struct dsa_port *dp;
|
|
|
|
struct device *dev;
|
|
|
|
const char *name;
|
2017-02-04 21:02:43 +00:00
|
|
|
unsigned int i;
|
2017-10-27 19:55:14 +00:00
|
|
|
int err;
|
2017-02-04 21:02:43 +00:00
|
|
|
|
|
|
|
for (i = 0; i < DSA_MAX_PORTS; i++) {
|
2017-10-27 19:55:14 +00:00
|
|
|
name = cd->port_names[i];
|
|
|
|
dev = cd->netdev[i];
|
2019-10-21 20:51:15 +00:00
|
|
|
dp = dsa_to_port(ds, i);
|
2017-10-27 19:55:14 +00:00
|
|
|
|
|
|
|
if (!name)
|
2017-02-04 21:02:43 +00:00
|
|
|
continue;
|
|
|
|
|
2017-10-27 19:55:14 +00:00
|
|
|
err = dsa_port_parse(dp, name, dev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-02-04 21:02:43 +00:00
|
|
|
valid_name_found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!valid_name_found && i == DSA_MAX_PORTS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
|
2017-02-04 21:02:43 +00:00
|
|
|
{
|
2019-10-21 20:51:16 +00:00
|
|
|
int err;
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
ds->cd = cd;
|
2017-02-04 21:02:43 +00:00
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
/* We don't support interconnected switches nor multiple trees via
|
|
|
|
* platform data, so this is the unique switch of the tree.
|
|
|
|
*/
|
|
|
|
ds->index = 0;
|
|
|
|
ds->dst = dsa_tree_touch(0);
|
|
|
|
if (!ds->dst)
|
|
|
|
return -ENOMEM;
|
2017-02-04 21:02:43 +00:00
|
|
|
|
2019-10-21 20:51:16 +00:00
|
|
|
err = dsa_switch_touch_ports(ds);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
return dsa_switch_parse_ports(ds, cd);
|
2017-02-04 21:02:43 +00:00
|
|
|
}
|
|
|
|
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
static void dsa_switch_release_ports(struct dsa_switch *ds)
|
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst = ds->dst;
|
|
|
|
struct dsa_port *dp, *next;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(dp, next, &dst->ports, list) {
|
|
|
|
if (dp->ds != ds)
|
|
|
|
continue;
|
|
|
|
list_del(&dp->list);
|
|
|
|
kfree(dp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:11:53 +00:00
|
|
|
static int dsa_switch_probe(struct dsa_switch *ds)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
2019-10-31 02:09:17 +00:00
|
|
|
struct dsa_switch_tree *dst;
|
2019-10-24 10:32:18 +00:00
|
|
|
struct dsa_chip_data *pdata;
|
|
|
|
struct device_node *np;
|
2017-11-06 21:11:51 +00:00
|
|
|
int err;
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2019-10-21 20:51:30 +00:00
|
|
|
if (!ds->dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2019-10-24 10:32:18 +00:00
|
|
|
pdata = ds->dev->platform_data;
|
|
|
|
np = ds->dev->of_node;
|
|
|
|
|
2019-10-21 20:51:30 +00:00
|
|
|
if (!ds->num_ports)
|
|
|
|
return -EINVAL;
|
|
|
|
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
if (np) {
|
2017-11-03 23:05:27 +00:00
|
|
|
err = dsa_switch_parse_of(ds, np);
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
if (err)
|
|
|
|
dsa_switch_release_ports(ds);
|
|
|
|
} else if (pdata) {
|
2017-11-03 23:05:27 +00:00
|
|
|
err = dsa_switch_parse(ds, pdata);
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
if (err)
|
|
|
|
dsa_switch_release_ports(ds);
|
|
|
|
} else {
|
2017-11-03 23:05:27 +00:00
|
|
|
err = -ENODEV;
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
}
|
2016-06-04 19:17:07 +00:00
|
|
|
|
2017-11-03 23:05:27 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
2016-07-07 00:03:54 +00:00
|
|
|
|
2019-10-31 02:09:17 +00:00
|
|
|
dst = ds->dst;
|
|
|
|
dsa_tree_get(dst);
|
|
|
|
err = dsa_tree_setup(dst);
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
if (err) {
|
|
|
|
dsa_switch_release_ports(ds);
|
2019-10-31 02:09:17 +00:00
|
|
|
dsa_tree_put(dst);
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
}
|
2019-10-31 02:09:17 +00:00
|
|
|
|
|
|
|
return err;
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
2017-05-26 22:12:51 +00:00
|
|
|
int dsa_register_switch(struct dsa_switch *ds)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
mutex_lock(&dsa2_mutex);
|
2017-11-06 21:11:53 +00:00
|
|
|
err = dsa_switch_probe(ds);
|
2017-11-24 16:36:06 +00:00
|
|
|
dsa_tree_put(ds->dst);
|
2016-06-04 19:17:07 +00:00
|
|
|
mutex_unlock(&dsa2_mutex);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dsa_register_switch);
|
|
|
|
|
2017-11-06 21:11:53 +00:00
|
|
|
static void dsa_switch_remove(struct dsa_switch *ds)
|
2016-06-04 19:17:07 +00:00
|
|
|
{
|
|
|
|
struct dsa_switch_tree *dst = ds->dst;
|
2019-10-21 20:51:29 +00:00
|
|
|
|
2019-11-03 03:13:26 +00:00
|
|
|
dsa_tree_teardown(dst);
|
net: dsa: Fix use-after-free in probing of DSA switch tree
DSA sets up a switch tree little by little. Every switch of the N
members of the tree calls dsa_register_switch, and (N - 1) will just
touch the dst->ports list with their ports and quickly exit. Only the
last switch that calls dsa_register_switch will find all DSA links
complete in dsa_tree_setup_routing_table, and not return zero as a
result but instead go ahead and set up the entire DSA switch tree
(practically on behalf of the other switches too).
The trouble is that the (N - 1) switches don't clean up after themselves
after they get an error such as EPROBE_DEFER. Their footprint left in
dst->ports by dsa_switch_touch_ports is still there. And switch N, the
one responsible with actually setting up the tree, is going to work with
those stale dp, dp->ds and dp->ds->dev pointers. In particular ds and
ds->dev might get freed by the device driver.
Be there a 2-switch tree and the following calling order:
- Switch 1 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Calls dsa_port_parse_cpu, gets -EPROBE_DEFER, exits.
- Switch 2 calls dsa_register_switch
- Calls dsa_switch_touch_ports, populates dst->ports
- Probe doesn't get deferred, so it goes ahead.
- Calls dsa_tree_setup_routing_table, which returns "complete == true"
due to Switch 1 having called dsa_switch_touch_ports before.
- Because the DSA links are complete, it calls dsa_tree_setup_switches
now.
- dsa_tree_setup_switches iterates through dst->ports, initializing
the Switch 1 ds structure (invalid) and the Switch 2 ds structure
(valid).
- Undefined behavior (use after free, sometimes NULL pointers, etc).
Real example below (debugging prints added by me, as well as guards
against NULL pointers):
[ 5.477947] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.313002] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.319932] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.329693] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.339458] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.349226] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.358991] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.368758] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.378524] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.388291] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.398057] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803df0b980 (dev ffffff803f775c00)
[ 6.407912] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.417682] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.427446] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.437212] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.446979] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.456744] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.466512] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.476277] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.486043] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.495810] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.505577] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803da02f80 (dev 0000000000000000)
[ 6.515433] dsa_tree_setup_switches: Setting up port 0 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.354120] dsa_tree_setup_switches: Setting up port 1 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.361045] dsa_tree_setup_switches: Setting up port 2 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.370805] dsa_tree_setup_switches: Setting up port 3 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.380571] dsa_tree_setup_switches: Setting up port 4 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.390337] dsa_tree_setup_switches: Setting up port 5 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.400104] dsa_tree_setup_switches: Setting up port 6 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.409872] dsa_tree_setup_switches: Setting up port 7 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.419637] dsa_tree_setup_switches: Setting up port 8 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.429403] dsa_tree_setup_switches: Setting up port 9 of switch ffffff803db15b80 (dev ffffff803d8e4800)
[ 7.439169] dsa_tree_setup_switches: Setting up port 10 of switch ffffff803db15b80 (dev ffffff803d8e4800)
The solution is to recognize that the functions that call
dsa_switch_touch_ports (dsa_switch_parse_of, dsa_switch_parse) have side
effects, and therefore one should clean up their side effects on error
path. The cleanup of dst->ports was taken from dsa_switch_remove and
moved into a dedicated dsa_switch_release_ports function, which should
really be per-switch (free only the members of dst->ports that are also
members of ds, instead of all switch ports).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-01-25 21:01:11 +00:00
|
|
|
dsa_switch_release_ports(ds);
|
2019-10-31 02:09:17 +00:00
|
|
|
dsa_tree_put(dst);
|
2016-06-04 19:17:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void dsa_unregister_switch(struct dsa_switch *ds)
|
|
|
|
{
|
|
|
|
mutex_lock(&dsa2_mutex);
|
2017-11-06 21:11:53 +00:00
|
|
|
dsa_switch_remove(ds);
|
2016-06-04 19:17:07 +00:00
|
|
|
mutex_unlock(&dsa2_mutex);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(dsa_unregister_switch);
|