net: dsa: Initialize CPU port ethtool ops per tree
authorFlorian Fainelli <f.fainelli@gmail.com>
Tue, 7 Jun 2016 23:32:42 +0000 (16:32 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 8 Jun 2016 18:23:42 +0000 (11:23 -0700)
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 <f.fainelli@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/dsa.h
net/dsa/dsa.c
net/dsa/dsa2.c
net/dsa/dsa_priv.h
net/dsa/slave.c

index cca7ef230742a38cac0878eafb183097522756bb..20b3087ad1933d2c731876387224bc509c8c6d47 100644 (file)
@@ -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.
index ce3b942dce76f6db4078ca1e556819fff2e86a60..766d2a525ada4dd35ec53736afc9fe5aea4502e4 100644 (file)
@@ -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;
@@ -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.
@@ -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);
 }
 
index 4e0f3c268103ca67ac025029452d3cd1531f3459..83b95fc4cede30d088a7ba795ad57629706c1876 100644 (file)
@@ -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);
        }
 
+       dsa_cpu_port_ethtool_restore(dst->ds[0]);
+
        pr_info("DSA: tree %d unapplied\n", dst->tree);
        dst->applied = false;
 }
index 106a9f067f94f8a2d12d2c0b99fa4c6a6460220a..00077a9c97f416d8cc6a167a22bc0a2053e7d6ab 100644 (file)
@@ -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;
index 8d159932e082728644cb4246a5a05171835cd2f8..7236eb26dc97a532c36c6aae7bad9efec1d1d41c 100644 (file)
@@ -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));
-               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;