Commit 0c73c523 authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

net: dsa: Initialize CPU port ethtool ops per tree

Now that we can properly support multiple distinct trees in the system,
using a global variable: dsa_cpu_port_ethtool_ops is getting clobbered
as soon as the second switch tree gets probed, and we don't want that.

We need to move this to be dynamically allocated, and since we can't
really be comparing addresses anymore to determine first time
initialization versus any other times, just move this to dsa.c and
dsa2.c where the remainder of the dst/ds initialization happens.

The operations teardown restores the master netdev's ethtool_ops to its
original ethtool_ops pointer (typically within the Ethernet driver)
Signed-off-by: default avatarFlorian Fainelli <>
Reviewed-by: default avatarAndrew Lunn <>
Signed-off-by: default avatarDavid S. Miller <>
parent af42192c
......@@ -116,6 +116,7 @@ struct dsa_switch_tree {
* Original copy of the master netdev ethtool_ops
struct ethtool_ops master_ethtool_ops;
const struct ethtool_ops *master_orig_ethtool_ops;
* The switch and port to which the CPU is attached.
......@@ -266,6 +266,41 @@ const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol)
return ops;
int dsa_cpu_port_ethtool_setup(struct dsa_switch *ds)
struct net_device *master;
struct ethtool_ops *cpu_ops;
master = ds->dst->master_netdev;
if (ds->master_netdev)
master = ds->master_netdev;
cpu_ops = devm_kzalloc(ds->dev, sizeof(*cpu_ops), GFP_KERNEL);
if (!cpu_ops)
return -ENOMEM;
memcpy(&ds->dst->master_ethtool_ops, master->ethtool_ops,
sizeof(struct ethtool_ops));
ds->dst->master_orig_ethtool_ops = master->ethtool_ops;
memcpy(cpu_ops, &ds->dst->master_ethtool_ops,
sizeof(struct ethtool_ops));
master->ethtool_ops = cpu_ops;
return 0;
void dsa_cpu_port_ethtool_restore(struct dsa_switch *ds)
struct net_device *master;
master = ds->dst->master_netdev;
if (ds->master_netdev)
master = ds->master_netdev;
master->ethtool_ops = ds->dst->master_orig_ethtool_ops;
static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
struct dsa_switch_driver *drv = ds->drv;
......@@ -379,6 +414,10 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)
ret = 0;
ret = dsa_cpu_port_ethtool_setup(ds);
if (ret)
return ret;
/* If the switch provides a temperature sensor,
* register with hardware monitoring subsystem.
......@@ -963,6 +1002,8 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
......@@ -394,6 +394,10 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst)
return err;
err = dsa_cpu_port_ethtool_setup(dst->ds[0]);
if (err)
return err;
/* If we use a tagging format that doesn't have an ethertype
* field, make sure that all packets from this point on get
* sent to the tag format's receive function.
......@@ -429,6 +433,8 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst)
dsa_ds_unapply(dst, ds);
pr_info("DSA: tree %d unapplied\n", dst->tree);
dst->applied = false;
......@@ -54,6 +54,8 @@ int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev,
struct device_node *port_dn, int port);
void dsa_cpu_dsa_destroy(struct device_node *port_dn);
const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol);
int dsa_cpu_port_ethtool_setup(struct dsa_switch *ds);
void dsa_cpu_port_ethtool_restore(struct dsa_switch *ds);
/* slave.c */
extern const struct dsa_device_ops notag_netdev_ops;
......@@ -892,8 +892,6 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_eee = dsa_slave_get_eee,
static struct ethtool_ops dsa_cpu_port_ethtool_ops;
static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_open = dsa_slave_open,
.ndo_stop = dsa_slave_close,
......@@ -1126,14 +1124,6 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
slave_dev->features = master->vlan_features;
slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
if (master->ethtool_ops != &dsa_cpu_port_ethtool_ops) {
memcpy(&dst->master_ethtool_ops, master->ethtool_ops,
sizeof(struct ethtool_ops));
memcpy(&dsa_cpu_port_ethtool_ops, &dst->master_ethtool_ops,
sizeof(struct ethtool_ops));
master->ethtool_ops = &dsa_cpu_port_ethtool_ops;
eth_hw_addr_inherit(slave_dev, master);
slave_dev->priv_flags |= IFF_NO_QUEUE;
slave_dev->netdev_ops = &dsa_slave_netdev_ops;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment