diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-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, | |||
172 | BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); | 172 | BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); |
173 | msgindex = rtm_msgindex(msgtype); | 173 | msgindex = rtm_msgindex(msgtype); |
174 | 174 | ||
175 | tab = rcu_dereference(rtnl_msg_handlers[protocol]); | 175 | tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]); |
176 | if (tab == NULL) { | 176 | if (tab == NULL) { |
177 | tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); | 177 | tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); |
178 | if (tab == NULL) | 178 | if (tab == NULL) |
@@ -262,7 +262,7 @@ void rtnl_unregister_all(int protocol) | |||
262 | 262 | ||
263 | synchronize_net(); | 263 | synchronize_net(); |
264 | 264 | ||
265 | while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 0) | 265 | while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1) |
266 | schedule(); | 266 | schedule(); |
267 | kfree(handlers); | 267 | kfree(handlers); |
268 | } | 268 | } |
@@ -402,16 +402,24 @@ static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev) | |||
402 | { | 402 | { |
403 | struct net_device *master_dev; | 403 | struct net_device *master_dev; |
404 | const struct rtnl_link_ops *ops; | 404 | const struct rtnl_link_ops *ops; |
405 | size_t size = 0; | ||
405 | 406 | ||
406 | master_dev = netdev_master_upper_dev_get((struct net_device *) dev); | 407 | rcu_read_lock(); |
408 | |||
409 | master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev); | ||
407 | if (!master_dev) | 410 | if (!master_dev) |
408 | return 0; | 411 | goto out; |
412 | |||
409 | ops = master_dev->rtnl_link_ops; | 413 | ops = master_dev->rtnl_link_ops; |
410 | if (!ops || !ops->get_slave_size) | 414 | if (!ops || !ops->get_slave_size) |
411 | return 0; | 415 | goto out; |
412 | /* IFLA_INFO_SLAVE_DATA + nested data */ | 416 | /* IFLA_INFO_SLAVE_DATA + nested data */ |
413 | return nla_total_size(sizeof(struct nlattr)) + | 417 | size = nla_total_size(sizeof(struct nlattr)) + |
414 | ops->get_slave_size(master_dev, dev); | 418 | ops->get_slave_size(master_dev, dev); |
419 | |||
420 | out: | ||
421 | rcu_read_unlock(); | ||
422 | return size; | ||
415 | } | 423 | } |
416 | 424 | ||
417 | static size_t rtnl_link_get_size(const struct net_device *dev) | 425 | 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, | |||
4167 | if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) | 4175 | if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) |
4168 | return -EPERM; | 4176 | return -EPERM; |
4169 | 4177 | ||
4170 | if (family > ARRAY_SIZE(rtnl_msg_handlers)) | 4178 | if (family >= ARRAY_SIZE(rtnl_msg_handlers)) |
4171 | family = PF_UNSPEC; | 4179 | family = PF_UNSPEC; |
4172 | 4180 | ||
4173 | rcu_read_lock(); | 4181 | rcu_read_lock(); |
@@ -4196,7 +4204,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4196 | 4204 | ||
4197 | refcount_inc(&rtnl_msg_handlers_ref[family]); | 4205 | refcount_inc(&rtnl_msg_handlers_ref[family]); |
4198 | 4206 | ||
4199 | if (type == RTM_GETLINK) | 4207 | if (type == RTM_GETLINK - RTM_BASE) |
4200 | min_dump_alloc = rtnl_calcit(skb, nlh); | 4208 | min_dump_alloc = rtnl_calcit(skb, nlh); |
4201 | 4209 | ||
4202 | rcu_read_unlock(); | 4210 | rcu_read_unlock(); |
@@ -4213,6 +4221,12 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
4213 | return err; | 4221 | return err; |
4214 | } | 4222 | } |
4215 | 4223 | ||
4224 | doit = READ_ONCE(handlers[type].doit); | ||
4225 | if (!doit) { | ||
4226 | family = PF_UNSPEC; | ||
4227 | handlers = rcu_dereference(rtnl_msg_handlers[family]); | ||
4228 | } | ||
4229 | |||
4216 | flags = READ_ONCE(handlers[type].flags); | 4230 | flags = READ_ONCE(handlers[type].flags); |
4217 | if (flags & RTNL_FLAG_DOIT_UNLOCKED) { | 4231 | if (flags & RTNL_FLAG_DOIT_UNLOCKED) { |
4218 | refcount_inc(&rtnl_msg_handlers_ref[family]); | 4232 | refcount_inc(&rtnl_msg_handlers_ref[family]); |
@@ -4316,6 +4330,11 @@ static struct pernet_operations rtnetlink_net_ops = { | |||
4316 | 4330 | ||
4317 | void __init rtnetlink_init(void) | 4331 | void __init rtnetlink_init(void) |
4318 | { | 4332 | { |
4333 | int i; | ||
4334 | |||
4335 | for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++) | ||
4336 | refcount_set(&rtnl_msg_handlers_ref[i], 1); | ||
4337 | |||
4319 | if (register_pernet_subsys(&rtnetlink_net_ops)) | 4338 | if (register_pernet_subsys(&rtnetlink_net_ops)) |
4320 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); | 4339 | panic("rtnetlink_init: cannot initialize rtnetlink\n"); |
4321 | 4340 | ||