diff options
author | David S. Miller <davem@davemloft.net> | 2017-04-11 22:22:45 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-04-11 22:22:45 -0400 |
commit | fc9c89b19c56813434c3d2967652a4fa6906926a (patch) | |
tree | 78c0c74da30e3525566ef791dc9330872dba71ba /net | |
parent | 96a94cc5158859943b7e4e72ae69e572815f5413 (diff) | |
parent | 5b8d5429daa05bebef6ffd3297df3b502cc6f184 (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_device.c | 20 | ||||
-rw-r--r-- | net/bridge/br_if.c | 1 | ||||
-rw-r--r-- | net/bridge/br_multicast.c | 7 | ||||
-rw-r--r-- | net/bridge/br_netlink.c | 7 | ||||
-rw-r--r-- | net/bridge/br_private.h | 5 |
5 files changed, 26 insertions, 14 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ea71513fca21..90f49a194249 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -119,6 +119,15 @@ static int br_dev_init(struct net_device *dev) | |||
119 | return err; | 119 | return err; |
120 | } | 120 | } |
121 | 121 | ||
122 | static void br_dev_uninit(struct net_device *dev) | ||
123 | { | ||
124 | struct net_bridge *br = netdev_priv(dev); | ||
125 | |||
126 | br_multicast_uninit_stats(br); | ||
127 | br_vlan_flush(br); | ||
128 | free_percpu(br->stats); | ||
129 | } | ||
130 | |||
122 | static int br_dev_open(struct net_device *dev) | 131 | static int br_dev_open(struct net_device *dev) |
123 | { | 132 | { |
124 | struct net_bridge *br = netdev_priv(dev); | 133 | struct net_bridge *br = netdev_priv(dev); |
@@ -332,6 +341,7 @@ static const struct net_device_ops br_netdev_ops = { | |||
332 | .ndo_open = br_dev_open, | 341 | .ndo_open = br_dev_open, |
333 | .ndo_stop = br_dev_stop, | 342 | .ndo_stop = br_dev_stop, |
334 | .ndo_init = br_dev_init, | 343 | .ndo_init = br_dev_init, |
344 | .ndo_uninit = br_dev_uninit, | ||
335 | .ndo_start_xmit = br_dev_xmit, | 345 | .ndo_start_xmit = br_dev_xmit, |
336 | .ndo_get_stats64 = br_get_stats64, | 346 | .ndo_get_stats64 = br_get_stats64, |
337 | .ndo_set_mac_address = br_set_mac_address, | 347 | .ndo_set_mac_address = br_set_mac_address, |
@@ -356,14 +366,6 @@ static const struct net_device_ops br_netdev_ops = { | |||
356 | .ndo_features_check = passthru_features_check, | 366 | .ndo_features_check = passthru_features_check, |
357 | }; | 367 | }; |
358 | 368 | ||
359 | static void br_dev_free(struct net_device *dev) | ||
360 | { | ||
361 | struct net_bridge *br = netdev_priv(dev); | ||
362 | |||
363 | free_percpu(br->stats); | ||
364 | free_netdev(dev); | ||
365 | } | ||
366 | |||
367 | static struct device_type br_type = { | 369 | static struct device_type br_type = { |
368 | .name = "bridge", | 370 | .name = "bridge", |
369 | }; | 371 | }; |
@@ -376,7 +378,7 @@ void br_dev_setup(struct net_device *dev) | |||
376 | ether_setup(dev); | 378 | ether_setup(dev); |
377 | 379 | ||
378 | dev->netdev_ops = &br_netdev_ops; | 380 | dev->netdev_ops = &br_netdev_ops; |
379 | dev->destructor = br_dev_free; | 381 | dev->destructor = free_netdev; |
380 | dev->ethtool_ops = &br_ethtool_ops; | 382 | dev->ethtool_ops = &br_ethtool_ops; |
381 | SET_NETDEV_DEVTYPE(dev, &br_type); | 383 | SET_NETDEV_DEVTYPE(dev, &br_type); |
382 | dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE; | 384 | dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE; |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 8ac1770aa222..56a2a72e7738 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
@@ -311,7 +311,6 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) | |||
311 | 311 | ||
312 | br_fdb_delete_by_port(br, NULL, 0, 1); | 312 | br_fdb_delete_by_port(br, NULL, 0, 1); |
313 | 313 | ||
314 | br_vlan_flush(br); | ||
315 | br_multicast_dev_del(br); | 314 | br_multicast_dev_del(br); |
316 | cancel_delayed_work_sync(&br->gc_work); | 315 | cancel_delayed_work_sync(&br->gc_work); |
317 | 316 | ||
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index b760f2620abf..faa7261a992f 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c | |||
@@ -2031,8 +2031,6 @@ void br_multicast_dev_del(struct net_bridge *br) | |||
2031 | 2031 | ||
2032 | out: | 2032 | out: |
2033 | spin_unlock_bh(&br->multicast_lock); | 2033 | spin_unlock_bh(&br->multicast_lock); |
2034 | |||
2035 | free_percpu(br->mcast_stats); | ||
2036 | } | 2034 | } |
2037 | 2035 | ||
2038 | int br_multicast_set_router(struct net_bridge *br, unsigned long val) | 2036 | 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) | |||
2531 | return 0; | 2529 | return 0; |
2532 | } | 2530 | } |
2533 | 2531 | ||
2532 | void br_multicast_uninit_stats(struct net_bridge *br) | ||
2533 | { | ||
2534 | free_percpu(br->mcast_stats); | ||
2535 | } | ||
2536 | |||
2534 | static void mcast_stats_add_dir(u64 *dst, u64 *src) | 2537 | static void mcast_stats_add_dir(u64 *dst, u64 *src) |
2535 | { | 2538 | { |
2536 | dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX]; | 2539 | dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX]; |
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index a8f6acd23e30..225ef7d53701 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c | |||
@@ -1165,11 +1165,14 @@ static int br_dev_newlink(struct net *src_net, struct net_device *dev, | |||
1165 | spin_unlock_bh(&br->lock); | 1165 | spin_unlock_bh(&br->lock); |
1166 | } | 1166 | } |
1167 | 1167 | ||
1168 | err = br_changelink(dev, tb, data); | 1168 | err = register_netdevice(dev); |
1169 | if (err) | 1169 | if (err) |
1170 | return err; | 1170 | return err; |
1171 | 1171 | ||
1172 | return register_netdevice(dev); | 1172 | err = br_changelink(dev, tb, data); |
1173 | if (err) | ||
1174 | unregister_netdevice(dev); | ||
1175 | return err; | ||
1173 | } | 1176 | } |
1174 | 1177 | ||
1175 | static size_t br_get_size(const struct net_device *brdev) | 1178 | static size_t br_get_size(const struct net_device *brdev) |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 61368186edea..0d177280aa84 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -620,6 +620,7 @@ void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, | |||
620 | void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p, | 620 | void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p, |
621 | const struct sk_buff *skb, u8 type, u8 dir); | 621 | const struct sk_buff *skb, u8 type, u8 dir); |
622 | int br_multicast_init_stats(struct net_bridge *br); | 622 | int br_multicast_init_stats(struct net_bridge *br); |
623 | void br_multicast_uninit_stats(struct net_bridge *br); | ||
623 | void br_multicast_get_stats(const struct net_bridge *br, | 624 | void br_multicast_get_stats(const struct net_bridge *br, |
624 | const struct net_bridge_port *p, | 625 | const struct net_bridge_port *p, |
625 | struct br_mcast_stats *dest); | 626 | struct br_mcast_stats *dest); |
@@ -760,6 +761,10 @@ static inline int br_multicast_init_stats(struct net_bridge *br) | |||
760 | return 0; | 761 | return 0; |
761 | } | 762 | } |
762 | 763 | ||
764 | static inline void br_multicast_uninit_stats(struct net_bridge *br) | ||
765 | { | ||
766 | } | ||
767 | |||
763 | static inline int br_multicast_igmp_type(const struct sk_buff *skb) | 768 | static inline int br_multicast_igmp_type(const struct sk_buff *skb) |
764 | { | 769 | { |
765 | return 0; | 770 | return 0; |