net: dsa: Ensure that port array elements are initialized before being used
authorGuenter Roeck <linux@roeck-us.net>
Tue, 24 Feb 2015 21:15:32 +0000 (13:15 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 25 Feb 2015 22:03:38 +0000 (17:03 -0500)
A network device notifier can be called for one or more of the created
slave devices before all slave devices have been registered. This can
result in a mismatch between ds->phys_port_mask and the registered devices
by the time the call is made, and it can result in a slave device being
added to a bridge before its entry in ds->ports[] has been initialized.

Rework the initialization code to initialize entries in ds->ports[] in
dsa_slave_create. With this change, dsa_slave_create no longer needs
to return slave_dev but can return an error code instead.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/dsa/dsa.c
net/dsa/dsa_priv.h
net/dsa/slave.c

index 2173402d87e0f56f255d0b378f06fc51aa2d3fed..fc1813140be69a88fc6ca060e5bb8ecba7cf5843 100644 (file)
@@ -314,19 +314,15 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
         * Create network devices for physical switch ports.
         */
        for (i = 0; i < DSA_MAX_PORTS; i++) {
-               struct net_device *slave_dev;
-
                if (!(ds->phys_port_mask & (1 << i)))
                        continue;
 
-               slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
-               if (slave_dev == NULL) {
+               ret = dsa_slave_create(ds, parent, i, pd->port_names[i]);
+               if (ret < 0) {
                        netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n",
                                   index, i, pd->port_names[i]);
-                       continue;
+                       ret = 0;
                }
-
-               ds->ports[i] = slave_dev;
        }
 
 #ifdef CONFIG_NET_DSA_HWMON
index dc9756d3154c7c81a1f8bd2b3124de36f0a97963..7eb1a6acd46c33a4df92c0d0baa8661ffd963761 100644 (file)
@@ -53,9 +53,8 @@ extern char dsa_driver_version[];
 /* slave.c */
 extern const struct dsa_device_ops notag_netdev_ops;
 void dsa_slave_mii_bus_init(struct dsa_switch *ds);
-struct net_device *dsa_slave_create(struct dsa_switch *ds,
-                                   struct device *parent,
-                                   int port, char *name);
+int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
+                    int port, char *name);
 int dsa_slave_suspend(struct net_device *slave_dev);
 int dsa_slave_resume(struct net_device *slave_dev);
 
index f23deadf42a070a251a7fc0c8f7b4d0a1e767dc5..5be4c928c9c994cfae7bd47a2a025d43335633d1 100644 (file)
@@ -605,9 +605,8 @@ int dsa_slave_resume(struct net_device *slave_dev)
        return 0;
 }
 
-struct net_device *
-dsa_slave_create(struct dsa_switch *ds, struct device *parent,
-                int port, char *name)
+int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
+                    int port, char *name)
 {
        struct net_device *master = ds->dst->master_netdev;
        struct net_device *slave_dev;
@@ -617,7 +616,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
        slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
                                 NET_NAME_UNKNOWN, ether_setup);
        if (slave_dev == NULL)
-               return slave_dev;
+               return -ENOMEM;
 
        slave_dev->features = master->vlan_features;
        slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
@@ -667,19 +666,21 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
        ret = dsa_slave_phy_setup(p, slave_dev);
        if (ret) {
                free_netdev(slave_dev);
-               return NULL;
+               return ret;
        }
 
+       ds->ports[port] = slave_dev;
        ret = register_netdev(slave_dev);
        if (ret) {
                netdev_err(master, "error %d registering interface %s\n",
                           ret, slave_dev->name);
                phy_disconnect(p->phy);
+               ds->ports[port] = NULL;
                free_netdev(slave_dev);
-               return NULL;
+               return ret;
        }
 
        netif_carrier_off(slave_dev);
 
-       return slave_dev;
+       return 0;
 }