diff options
author | Pravin B Shelar <pshelar@nicira.com> | 2013-08-23 15:44:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-08-28 17:19:17 -0400 |
commit | 9b96309c5b0b9e466773c07a5bc8b7b68fcf010a (patch) | |
tree | b41719289b4aaaf7a176f1d94924e2d1e8528cfe /net/netlink/genetlink.c | |
parent | a3097bda78c7fb41fd3091ffb70bf7bd946e6997 (diff) |
genl: Fix genl dumpit() locking.
In case of genl-family with parallel ops off, dumpif() callback
is expected to run under genl_lock, But commit def3117493eafd9df
(genl: Allow concurrent genl callbacks.) changed this behaviour
where only first dumpit() op was called under genl-lock.
For subsequent dump, only nlk->cb_lock was taken.
Following patch fixes it by defining locked dumpit() and done()
callback which takes care of genl-locking.
CC: Jesse Gross <jesse@nicira.com>
CC: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink/genetlink.c')
-rw-r--r-- | net/netlink/genetlink.c | 51 |
1 files changed, 46 insertions, 5 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 512718adb0d5..7e0f4d199ade 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c | |||
@@ -544,6 +544,30 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, | |||
544 | } | 544 | } |
545 | EXPORT_SYMBOL(genlmsg_put); | 545 | EXPORT_SYMBOL(genlmsg_put); |
546 | 546 | ||
547 | static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) | ||
548 | { | ||
549 | struct genl_ops *ops = cb->data; | ||
550 | int rc; | ||
551 | |||
552 | genl_lock(); | ||
553 | rc = ops->dumpit(skb, cb); | ||
554 | genl_unlock(); | ||
555 | return rc; | ||
556 | } | ||
557 | |||
558 | static int genl_lock_done(struct netlink_callback *cb) | ||
559 | { | ||
560 | struct genl_ops *ops = cb->data; | ||
561 | int rc = 0; | ||
562 | |||
563 | if (ops->done) { | ||
564 | genl_lock(); | ||
565 | rc = ops->done(cb); | ||
566 | genl_unlock(); | ||
567 | } | ||
568 | return rc; | ||
569 | } | ||
570 | |||
547 | static int genl_family_rcv_msg(struct genl_family *family, | 571 | static int genl_family_rcv_msg(struct genl_family *family, |
548 | struct sk_buff *skb, | 572 | struct sk_buff *skb, |
549 | struct nlmsghdr *nlh) | 573 | struct nlmsghdr *nlh) |
@@ -572,15 +596,32 @@ static int genl_family_rcv_msg(struct genl_family *family, | |||
572 | return -EPERM; | 596 | return -EPERM; |
573 | 597 | ||
574 | if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { | 598 | if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { |
575 | struct netlink_dump_control c = { | 599 | int rc; |
576 | .dump = ops->dumpit, | ||
577 | .done = ops->done, | ||
578 | }; | ||
579 | 600 | ||
580 | if (ops->dumpit == NULL) | 601 | if (ops->dumpit == NULL) |
581 | return -EOPNOTSUPP; | 602 | return -EOPNOTSUPP; |
582 | 603 | ||
583 | return netlink_dump_start(net->genl_sock, skb, nlh, &c); | 604 | if (!family->parallel_ops) { |
605 | struct netlink_dump_control c = { | ||
606 | .data = ops, | ||
607 | .dump = genl_lock_dumpit, | ||
608 | .done = genl_lock_done, | ||
609 | }; | ||
610 | |||
611 | genl_unlock(); | ||
612 | rc = netlink_dump_start(net->genl_sock, skb, nlh, &c); | ||
613 | genl_lock(); | ||
614 | |||
615 | } else { | ||
616 | struct netlink_dump_control c = { | ||
617 | .dump = ops->dumpit, | ||
618 | .done = ops->done, | ||
619 | }; | ||
620 | |||
621 | rc = netlink_dump_start(net->genl_sock, skb, nlh, &c); | ||
622 | } | ||
623 | |||
624 | return rc; | ||
584 | } | 625 | } |
585 | 626 | ||
586 | if (ops->doit == NULL) | 627 | if (ops->doit == NULL) |