aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink
diff options
context:
space:
mode:
authorPravin B Shelar <pshelar@nicira.com>2013-04-23 03:48:30 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-25 01:43:15 -0400
commitdef3117493eafd9dfa1f809d861e0031b2cc8a07 (patch)
tree93edfbb60f9d57d37d5643062cf3d41befc18579 /net/netlink
parent133b94245c54bb2d8832bfb15975b931bc00d914 (diff)
genl: Allow concurrent genl callbacks.
All genl callbacks are serialized by genl-mutex. This can become bottleneck in multi threaded case. Following patch adds an parameter to genl_family so that a particular family can get concurrent netlink callback without genl_lock held. New rw-sem is used to protect genl callback from genl family unregister. in case of parallel_ops genl-family read-lock is taken for callbacks and write lock is taken for register or unregistration for any family. In case of locked genl family semaphore and gel-mutex is locked for any openration. Signed-off-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r--net/netlink/genetlink.c114
1 files changed, 76 insertions, 38 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 5a55be3f17a5..2f72598dd8fe 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -16,10 +16,12 @@
16#include <linux/skbuff.h> 16#include <linux/skbuff.h>
17#include <linux/mutex.h> 17#include <linux/mutex.h>
18#include <linux/bitmap.h> 18#include <linux/bitmap.h>
19#include <linux/rwsem.h>
19#include <net/sock.h> 20#include <net/sock.h>
20#include <net/genetlink.h> 21#include <net/genetlink.h>
21 22
22static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ 23static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */
24static DECLARE_RWSEM(cb_lock);
23 25
24void genl_lock(void) 26void genl_lock(void)
25{ 27{
@@ -41,6 +43,18 @@ int lockdep_genl_is_held(void)
41EXPORT_SYMBOL(lockdep_genl_is_held); 43EXPORT_SYMBOL(lockdep_genl_is_held);
42#endif 44#endif
43 45
46static void genl_lock_all(void)
47{
48 down_write(&cb_lock);
49 genl_lock();
50}
51
52static void genl_unlock_all(void)
53{
54 genl_unlock();
55 up_write(&cb_lock);
56}
57
44#define GENL_FAM_TAB_SIZE 16 58#define GENL_FAM_TAB_SIZE 16
45#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1) 59#define GENL_FAM_TAB_MASK (GENL_FAM_TAB_SIZE - 1)
46 60
@@ -144,7 +158,7 @@ int genl_register_mc_group(struct genl_family *family,
144 BUG_ON(grp->name[0] == '\0'); 158 BUG_ON(grp->name[0] == '\0');
145 BUG_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL); 159 BUG_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL);
146 160
147 genl_lock(); 161 genl_lock_all();
148 162
149 /* special-case our own group */ 163 /* special-case our own group */
150 if (grp == &notify_grp) 164 if (grp == &notify_grp)
@@ -213,7 +227,7 @@ int genl_register_mc_group(struct genl_family *family,
213 227
214 genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp); 228 genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, grp);
215 out: 229 out:
216 genl_unlock(); 230 genl_unlock_all();
217 return err; 231 return err;
218} 232}
219EXPORT_SYMBOL(genl_register_mc_group); 233EXPORT_SYMBOL(genl_register_mc_group);
@@ -255,9 +269,9 @@ static void __genl_unregister_mc_group(struct genl_family *family,
255void genl_unregister_mc_group(struct genl_family *family, 269void genl_unregister_mc_group(struct genl_family *family,
256 struct genl_multicast_group *grp) 270 struct genl_multicast_group *grp)
257{ 271{
258 genl_lock(); 272 genl_lock_all();
259 __genl_unregister_mc_group(family, grp); 273 __genl_unregister_mc_group(family, grp);
260 genl_unlock(); 274 genl_unlock_all();
261} 275}
262EXPORT_SYMBOL(genl_unregister_mc_group); 276EXPORT_SYMBOL(genl_unregister_mc_group);
263 277
@@ -303,9 +317,9 @@ int genl_register_ops(struct genl_family *family, struct genl_ops *ops)
303 if (ops->policy) 317 if (ops->policy)
304 ops->flags |= GENL_CMD_CAP_HASPOL; 318 ops->flags |= GENL_CMD_CAP_HASPOL;
305 319
306 genl_lock(); 320 genl_lock_all();
307 list_add_tail(&ops->ops_list, &family->ops_list); 321 list_add_tail(&ops->ops_list, &family->ops_list);
308 genl_unlock(); 322 genl_unlock_all();
309 323
310 genl_ctrl_event(CTRL_CMD_NEWOPS, ops); 324 genl_ctrl_event(CTRL_CMD_NEWOPS, ops);
311 err = 0; 325 err = 0;
@@ -334,16 +348,16 @@ int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops)
334{ 348{
335 struct genl_ops *rc; 349 struct genl_ops *rc;
336 350
337 genl_lock(); 351 genl_lock_all();
338 list_for_each_entry(rc, &family->ops_list, ops_list) { 352 list_for_each_entry(rc, &family->ops_list, ops_list) {
339 if (rc == ops) { 353 if (rc == ops) {
340 list_del(&ops->ops_list); 354 list_del(&ops->ops_list);
341 genl_unlock(); 355 genl_unlock_all();
342 genl_ctrl_event(CTRL_CMD_DELOPS, ops); 356 genl_ctrl_event(CTRL_CMD_DELOPS, ops);
343 return 0; 357 return 0;
344 } 358 }
345 } 359 }
346 genl_unlock(); 360 genl_unlock_all();
347 361
348 return -ENOENT; 362 return -ENOENT;
349} 363}
@@ -373,7 +387,7 @@ int genl_register_family(struct genl_family *family)
373 INIT_LIST_HEAD(&family->ops_list); 387 INIT_LIST_HEAD(&family->ops_list);
374 INIT_LIST_HEAD(&family->mcast_groups); 388 INIT_LIST_HEAD(&family->mcast_groups);
375 389
376 genl_lock(); 390 genl_lock_all();
377 391
378 if (genl_family_find_byname(family->name)) { 392 if (genl_family_find_byname(family->name)) {
379 err = -EEXIST; 393 err = -EEXIST;
@@ -394,7 +408,7 @@ int genl_register_family(struct genl_family *family)
394 goto errout_locked; 408 goto errout_locked;
395 } 409 }
396 410
397 if (family->maxattr) { 411 if (family->maxattr && !family->parallel_ops) {
398 family->attrbuf = kmalloc((family->maxattr+1) * 412 family->attrbuf = kmalloc((family->maxattr+1) *
399 sizeof(struct nlattr *), GFP_KERNEL); 413 sizeof(struct nlattr *), GFP_KERNEL);
400 if (family->attrbuf == NULL) { 414 if (family->attrbuf == NULL) {
@@ -405,14 +419,14 @@ int genl_register_family(struct genl_family *family)
405 family->attrbuf = NULL; 419 family->attrbuf = NULL;
406 420
407 list_add_tail(&family->family_list, genl_family_chain(family->id)); 421 list_add_tail(&family->family_list, genl_family_chain(family->id));
408 genl_unlock(); 422 genl_unlock_all();
409 423
410 genl_ctrl_event(CTRL_CMD_NEWFAMILY, family); 424 genl_ctrl_event(CTRL_CMD_NEWFAMILY, family);
411 425
412 return 0; 426 return 0;
413 427
414errout_locked: 428errout_locked:
415 genl_unlock(); 429 genl_unlock_all();
416errout: 430errout:
417 return err; 431 return err;
418} 432}
@@ -476,7 +490,7 @@ int genl_unregister_family(struct genl_family *family)
476{ 490{
477 struct genl_family *rc; 491 struct genl_family *rc;
478 492
479 genl_lock(); 493 genl_lock_all();
480 494
481 genl_unregister_mc_groups(family); 495 genl_unregister_mc_groups(family);
482 496
@@ -486,14 +500,14 @@ int genl_unregister_family(struct genl_family *family)
486 500
487 list_del(&rc->family_list); 501 list_del(&rc->family_list);
488 INIT_LIST_HEAD(&family->ops_list); 502 INIT_LIST_HEAD(&family->ops_list);
489 genl_unlock(); 503 genl_unlock_all();
490 504
491 kfree(family->attrbuf); 505 kfree(family->attrbuf);
492 genl_ctrl_event(CTRL_CMD_DELFAMILY, family); 506 genl_ctrl_event(CTRL_CMD_DELFAMILY, family);
493 return 0; 507 return 0;
494 } 508 }
495 509
496 genl_unlock(); 510 genl_unlock_all();
497 511
498 return -ENOENT; 512 return -ENOENT;
499} 513}
@@ -530,19 +544,17 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
530} 544}
531EXPORT_SYMBOL(genlmsg_put); 545EXPORT_SYMBOL(genlmsg_put);
532 546
533static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 547static int genl_family_rcv_msg(struct genl_family *family,
548 struct sk_buff *skb,
549 struct nlmsghdr *nlh)
534{ 550{
535 struct genl_ops *ops; 551 struct genl_ops *ops;
536 struct genl_family *family;
537 struct net *net = sock_net(skb->sk); 552 struct net *net = sock_net(skb->sk);
538 struct genl_info info; 553 struct genl_info info;
539 struct genlmsghdr *hdr = nlmsg_data(nlh); 554 struct genlmsghdr *hdr = nlmsg_data(nlh);
555 struct nlattr **attrbuf;
540 int hdrlen, err; 556 int hdrlen, err;
541 557
542 family = genl_family_find_byid(nlh->nlmsg_type);
543 if (family == NULL)
544 return -ENOENT;
545
546 /* this family doesn't exist in this netns */ 558 /* this family doesn't exist in this netns */
547 if (!family->netnsok && !net_eq(net, &init_net)) 559 if (!family->netnsok && !net_eq(net, &init_net))
548 return -ENOENT; 560 return -ENOENT;
@@ -560,26 +572,30 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
560 return -EPERM; 572 return -EPERM;
561 573
562 if (nlh->nlmsg_flags & NLM_F_DUMP) { 574 if (nlh->nlmsg_flags & NLM_F_DUMP) {
575 struct netlink_dump_control c = {
576 .dump = ops->dumpit,
577 .done = ops->done,
578 };
579
563 if (ops->dumpit == NULL) 580 if (ops->dumpit == NULL)
564 return -EOPNOTSUPP; 581 return -EOPNOTSUPP;
565 582
566 genl_unlock(); 583 return netlink_dump_start(net->genl_sock, skb, nlh, &c);
567 {
568 struct netlink_dump_control c = {
569 .dump = ops->dumpit,
570 .done = ops->done,
571 };
572 err = netlink_dump_start(net->genl_sock, skb, nlh, &c);
573 }
574 genl_lock();
575 return err;
576 } 584 }
577 585
578 if (ops->doit == NULL) 586 if (ops->doit == NULL)
579 return -EOPNOTSUPP; 587 return -EOPNOTSUPP;
580 588
581 if (family->attrbuf) { 589 if (family->maxattr && family->parallel_ops) {
582 err = nlmsg_parse(nlh, hdrlen, family->attrbuf, family->maxattr, 590 attrbuf = kmalloc((family->maxattr+1) *
591 sizeof(struct nlattr *), GFP_KERNEL);
592 if (attrbuf == NULL)
593 return -ENOMEM;
594 } else
595 attrbuf = family->attrbuf;
596
597 if (attrbuf) {
598 err = nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr,
583 ops->policy); 599 ops->policy);
584 if (err < 0) 600 if (err < 0)
585 return err; 601 return err;
@@ -590,7 +606,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
590 info.nlhdr = nlh; 606 info.nlhdr = nlh;
591 info.genlhdr = nlmsg_data(nlh); 607 info.genlhdr = nlmsg_data(nlh);
592 info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; 608 info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN;
593 info.attrs = family->attrbuf; 609 info.attrs = attrbuf;
594 genl_info_net_set(&info, net); 610 genl_info_net_set(&info, net);
595 memset(&info.user_ptr, 0, sizeof(info.user_ptr)); 611 memset(&info.user_ptr, 0, sizeof(info.user_ptr));
596 612
@@ -605,14 +621,37 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
605 if (family->post_doit) 621 if (family->post_doit)
606 family->post_doit(ops, skb, &info); 622 family->post_doit(ops, skb, &info);
607 623
624 if (family->parallel_ops)
625 kfree(attrbuf);
626
627 return err;
628}
629
630static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
631{
632 struct genl_family *family;
633 int err;
634
635 family = genl_family_find_byid(nlh->nlmsg_type);
636 if (family == NULL)
637 return -ENOENT;
638
639 if (!family->parallel_ops)
640 genl_lock();
641
642 err = genl_family_rcv_msg(family, skb, nlh);
643
644 if (!family->parallel_ops)
645 genl_unlock();
646
608 return err; 647 return err;
609} 648}
610 649
611static void genl_rcv(struct sk_buff *skb) 650static void genl_rcv(struct sk_buff *skb)
612{ 651{
613 genl_lock(); 652 down_read(&cb_lock);
614 netlink_rcv_skb(skb, &genl_rcv_msg); 653 netlink_rcv_skb(skb, &genl_rcv_msg);
615 genl_unlock(); 654 up_read(&cb_lock);
616} 655}
617 656
618/************************************************************************** 657/**************************************************************************
@@ -918,7 +957,6 @@ static int __net_init genl_pernet_init(struct net *net)
918{ 957{
919 struct netlink_kernel_cfg cfg = { 958 struct netlink_kernel_cfg cfg = {
920 .input = genl_rcv, 959 .input = genl_rcv,
921 .cb_mutex = &genl_mutex,
922 .flags = NL_CFG_F_NONROOT_RECV, 960 .flags = NL_CFG_F_NONROOT_RECV,
923 }; 961 };
924 962