Commit fc9c89b1 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 96a94cc5 5b8d5429
...@@ -119,6 +119,15 @@ static int br_dev_init(struct net_device *dev) ...@@ -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 = { ...@@ -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 = { ...@@ -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) ...@@ -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;
......
...@@ -311,7 +311,6 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) ...@@ -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);
......
...@@ -2031,8 +2031,6 @@ void br_multicast_dev_del(struct net_bridge *br) ...@@ -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) ...@@ -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];
......
...@@ -1165,11 +1165,14 @@ static int br_dev_newlink(struct net *src_net, struct net_device *dev, ...@@ -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)
......
...@@ -620,6 +620,7 @@ void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port, ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment