Sfoglia il codice sorgente

Merge branch 'bridge-register-netdev-before-changelink'

Ido Schimmel says:

====================
bridge: Fix kernel oops during bridge creation

First patch adds a missing ndo_uninit() in the bridge driver, which is a
prerequisite for the second patch that actually fixes the oops.

Please consider both patches for 4.4.y, 4.9.y and 4.10.y
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 8 anni fa
parent
commit
fc9c89b19c

+ 11 - 9
net/bridge/br_device.c

@@ -119,6 +119,15 @@ static int br_dev_init(struct net_device *dev)
 	return err;
 	return err;
 }
 }
 
 
+static void br_dev_uninit(struct net_device *dev)
+{
+	struct net_bridge *br = netdev_priv(dev);
+
+	br_multicast_uninit_stats(br);
+	br_vlan_flush(br);
+	free_percpu(br->stats);
+}
+
 static int br_dev_open(struct net_device *dev)
 static int br_dev_open(struct net_device *dev)
 {
 {
 	struct net_bridge *br = netdev_priv(dev);
 	struct net_bridge *br = netdev_priv(dev);
@@ -332,6 +341,7 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_open		 = br_dev_open,
 	.ndo_open		 = br_dev_open,
 	.ndo_stop		 = br_dev_stop,
 	.ndo_stop		 = br_dev_stop,
 	.ndo_init		 = br_dev_init,
 	.ndo_init		 = br_dev_init,
+	.ndo_uninit		 = br_dev_uninit,
 	.ndo_start_xmit		 = br_dev_xmit,
 	.ndo_start_xmit		 = br_dev_xmit,
 	.ndo_get_stats64	 = br_get_stats64,
 	.ndo_get_stats64	 = br_get_stats64,
 	.ndo_set_mac_address	 = br_set_mac_address,
 	.ndo_set_mac_address	 = br_set_mac_address,
@@ -356,14 +366,6 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_features_check	 = passthru_features_check,
 	.ndo_features_check	 = passthru_features_check,
 };
 };
 
 
-static void br_dev_free(struct net_device *dev)
-{
-	struct net_bridge *br = netdev_priv(dev);
-
-	free_percpu(br->stats);
-	free_netdev(dev);
-}
-
 static struct device_type br_type = {
 static struct device_type br_type = {
 	.name	= "bridge",
 	.name	= "bridge",
 };
 };
@@ -376,7 +378,7 @@ void br_dev_setup(struct net_device *dev)
 	ether_setup(dev);
 	ether_setup(dev);
 
 
 	dev->netdev_ops = &br_netdev_ops;
 	dev->netdev_ops = &br_netdev_ops;
-	dev->destructor = br_dev_free;
+	dev->destructor = free_netdev;
 	dev->ethtool_ops = &br_ethtool_ops;
 	dev->ethtool_ops = &br_ethtool_ops;
 	SET_NETDEV_DEVTYPE(dev, &br_type);
 	SET_NETDEV_DEVTYPE(dev, &br_type);
 	dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE;
 	dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE;

+ 0 - 1
net/bridge/br_if.c

@@ -311,7 +311,6 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
 
 
 	br_fdb_delete_by_port(br, NULL, 0, 1);
 	br_fdb_delete_by_port(br, NULL, 0, 1);
 
 
-	br_vlan_flush(br);
 	br_multicast_dev_del(br);
 	br_multicast_dev_del(br);
 	cancel_delayed_work_sync(&br->gc_work);
 	cancel_delayed_work_sync(&br->gc_work);
 
 

+ 5 - 2
net/bridge/br_multicast.c

@@ -2031,8 +2031,6 @@ void br_multicast_dev_del(struct net_bridge *br)
 
 
 out:
 out:
 	spin_unlock_bh(&br->multicast_lock);
 	spin_unlock_bh(&br->multicast_lock);
-
-	free_percpu(br->mcast_stats);
 }
 }
 
 
 int br_multicast_set_router(struct net_bridge *br, unsigned long val)
 int br_multicast_set_router(struct net_bridge *br, unsigned long val)
@@ -2531,6 +2529,11 @@ int br_multicast_init_stats(struct net_bridge *br)
 	return 0;
 	return 0;
 }
 }
 
 
+void br_multicast_uninit_stats(struct net_bridge *br)
+{
+	free_percpu(br->mcast_stats);
+}
+
 static void mcast_stats_add_dir(u64 *dst, u64 *src)
 static void mcast_stats_add_dir(u64 *dst, u64 *src)
 {
 {
 	dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX];
 	dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX];

+ 5 - 2
net/bridge/br_netlink.c

@@ -1165,11 +1165,14 @@ static int br_dev_newlink(struct net *src_net, struct net_device *dev,
 		spin_unlock_bh(&br->lock);
 		spin_unlock_bh(&br->lock);
 	}
 	}
 
 
-	err = br_changelink(dev, tb, data);
+	err = register_netdevice(dev);
 	if (err)
 	if (err)
 		return err;
 		return err;
 
 
-	return register_netdevice(dev);
+	err = br_changelink(dev, tb, data);
+	if (err)
+		unregister_netdevice(dev);
+	return err;
 }
 }
 
 
 static size_t br_get_size(const struct net_device *brdev)
 static size_t br_get_size(const struct net_device *brdev)

+ 5 - 0
net/bridge/br_private.h

@@ -620,6 +620,7 @@ void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
 void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
 void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
 			const struct sk_buff *skb, u8 type, u8 dir);
 			const struct sk_buff *skb, u8 type, u8 dir);
 int br_multicast_init_stats(struct net_bridge *br);
 int br_multicast_init_stats(struct net_bridge *br);
+void br_multicast_uninit_stats(struct net_bridge *br);
 void br_multicast_get_stats(const struct net_bridge *br,
 void br_multicast_get_stats(const struct net_bridge *br,
 			    const struct net_bridge_port *p,
 			    const struct net_bridge_port *p,
 			    struct br_mcast_stats *dest);
 			    struct br_mcast_stats *dest);
@@ -760,6 +761,10 @@ static inline int br_multicast_init_stats(struct net_bridge *br)
 	return 0;
 	return 0;
 }
 }
 
 
+static inline void br_multicast_uninit_stats(struct net_bridge *br)
+{
+}
+
 static inline int br_multicast_igmp_type(const struct sk_buff *skb)
 static inline int br_multicast_igmp_type(const struct sk_buff *skb)
 {
 {
 	return 0;
 	return 0;