diff options
author | David S. Miller <davem@davemloft.net> | 2017-08-10 09:50:22 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-08-10 09:50:22 -0700 |
commit | 2e2d5d767c9ddb2b10d74f2c20a257101c7070eb (patch) | |
tree | 0059c050729eacf01ebc6bb3691b46343ddab376 /net | |
parent | 2d5716456404a1ba097d46770f82f23a2457a873 (diff) | |
parent | 33b01b7b4f19f82198a298936de225eef942fc7c (diff) | |
download | lwn-2e2d5d767c9ddb2b10d74f2c20a257101c7070eb.tar.gz lwn-2e2d5d767c9ddb2b10d74f2c20a257101c7070eb.zip |
Merge branch 'rtnetlink-fix-initial-rtnl-pushdown-fallout'
Florian Westphal says:
====================
rtnetlink: fix initial rtnl pushdown fallout
This series fixes various bugs and splats reported since the
allow-handler-to-run-with-no-rtnl series went in.
Last patch adds a script that can be used to add further
tests in case more bugs are reported.
In case you prefer reverting the original series instead of
fixing fallout I can resend this patch on its own.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/rtnetlink.c | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index dd4e50dfa248..9e9f1419be60 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -172,7 +172,7 @@ int __rtnl_register(int protocol, int msgtype, BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); msgindex = rtm_msgindex(msgtype); - tab = rcu_dereference(rtnl_msg_handlers[protocol]); + tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]); if (tab == NULL) { tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); if (tab == NULL) @@ -262,7 +262,7 @@ void rtnl_unregister_all(int protocol) synchronize_net(); - while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 0) + while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1) schedule(); kfree(handlers); } @@ -402,16 +402,24 @@ static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev) { struct net_device *master_dev; const struct rtnl_link_ops *ops; + size_t size = 0; - master_dev = netdev_master_upper_dev_get((struct net_device *) dev); + rcu_read_lock(); + + master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev); if (!master_dev) - return 0; + goto out; + ops = master_dev->rtnl_link_ops; if (!ops || !ops->get_slave_size) - return 0; + goto out; /* IFLA_INFO_SLAVE_DATA + nested data */ - return nla_total_size(sizeof(struct nlattr)) + + size = nla_total_size(sizeof(struct nlattr)) + ops->get_slave_size(master_dev, dev); + +out: + rcu_read_unlock(); + return size; } static size_t rtnl_link_get_size(const struct net_device *dev) @@ -4167,7 +4175,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) return -EPERM; - if (family > ARRAY_SIZE(rtnl_msg_handlers)) + if (family >= ARRAY_SIZE(rtnl_msg_handlers)) family = PF_UNSPEC; rcu_read_lock(); @@ -4196,7 +4204,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, refcount_inc(&rtnl_msg_handlers_ref[family]); - if (type == RTM_GETLINK) + if (type == RTM_GETLINK - RTM_BASE) min_dump_alloc = rtnl_calcit(skb, nlh); rcu_read_unlock(); @@ -4213,6 +4221,12 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, return err; } + doit = READ_ONCE(handlers[type].doit); + if (!doit) { + family = PF_UNSPEC; + handlers = rcu_dereference(rtnl_msg_handlers[family]); + } + flags = READ_ONCE(handlers[type].flags); if (flags & RTNL_FLAG_DOIT_UNLOCKED) { refcount_inc(&rtnl_msg_handlers_ref[family]); @@ -4316,6 +4330,11 @@ static struct pernet_operations rtnetlink_net_ops = { void __init rtnetlink_init(void) { + int i; + + for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++) + refcount_set(&rtnl_msg_handlers_ref[i], 1); + if (register_pernet_subsys(&rtnetlink_net_ops)) panic("rtnetlink_init: cannot initialize rtnetlink\n"); |