diff options
author | Cong Wang <cwang@twopensource.com> | 2014-02-11 15:51:30 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-02-13 17:08:29 -0500 |
commit | 0e0eee2465df77bcec2e8ff75432b8e57897b143 (patch) | |
tree | 8d0728743356a1f524b6f88f09a1b09016d56190 /net | |
parent | da37705cef30841616ed644ff33455bbc7374db0 (diff) | |
download | linux-0e0eee2465df77bcec2e8ff75432b8e57897b143.tar.gz linux-0e0eee2465df77bcec2e8ff75432b8e57897b143.tar.bz2 linux-0e0eee2465df77bcec2e8ff75432b8e57897b143.zip |
net: correct error path in rtnl_newlink()
I saw the following BUG when ->newlink() fails in rtnl_newlink():
[ 40.240058] kernel BUG at net/core/dev.c:6438!
this is due to free_netdev() is not supposed to be called before
netdev is completely unregistered, therefore it is not correct
to call free_netdev() here, at least for ops->newlink!=NULL case,
many drivers call it in ->destructor so that rtnl_unlock() will
take care of it, we probably don't need to do anything here.
Cc: David S. Miller <davem@davemloft.net>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Cong Wang <cwang@twopensource.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/rtnetlink.c | 19 |
1 files changed, 12 insertions, 7 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 048dc8d183aa..1a0dac2ef9ad 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1963,16 +1963,21 @@ replay: dev->ifindex = ifm->ifi_index; - if (ops->newlink) + if (ops->newlink) { err = ops->newlink(net, dev, tb, data); - else + /* Drivers should call free_netdev() in ->destructor + * and unregister it on failure so that device could be + * finally freed in rtnl_unlock. + */ + if (err < 0) + goto out; + } else { err = register_netdevice(dev); - - if (err < 0) { - free_netdev(dev); - goto out; + if (err < 0) { + free_netdev(dev); + goto out; + } } - err = rtnl_configure_link(dev, ifm); if (err < 0) unregister_netdevice(dev); |