Skip to content
/ linux Public
forked from torvalds/linux

Commit

Permalink
net: dsa: Initialize CPU port ethtool ops per tree
Browse files Browse the repository at this point in the history
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: Florian Fainelli <[email protected]>
Reviewed-by: Andrew Lunn <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
ffainelli authored and davem330 committed Jun 8, 2016
1 parent af42192 commit 0c73c52
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 10 deletions.
1 change: 1 addition & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
41 changes: 41 additions & 0 deletions net/dsa/dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
dsa_cpu_port_ethtool_init(cpu_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;
Expand Down Expand Up @@ -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;

#ifdef CONFIG_NET_DSA_HWMON
/* If the switch provides a temperature sensor,
* register with hardware monitoring subsystem.
Expand Down Expand Up @@ -963,6 +1002,8 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
dsa_switch_destroy(ds);
}

dsa_cpu_port_ethtool_restore(dst->ds[0]);

dev_put(dst->master_netdev);
}

Expand Down
6 changes: 6 additions & 0 deletions net/dsa/dsa2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -429,6 +433,8 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst)
dsa_ds_unapply(dst, ds);
}

dsa_cpu_port_ethtool_restore(dst->ds[0]);

pr_info("DSA: tree %d unapplied\n", dst->tree);
dst->applied = false;
}
Expand Down
2 changes: 2 additions & 0 deletions net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 0 additions & 10 deletions net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
dsa_cpu_port_ethtool_init(&dsa_cpu_port_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;
Expand Down

0 comments on commit 0c73c52

Please sign in to comment.