diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-11-11 23:11:50 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-13 23:46:57 -0500 |
commit | 234b27c3fd58fc0e15c04dd0fbf4337fac9c2a06 (patch) | |
tree | b7cc69200bee85872e2b8bf7fa93d2c8b519de2e /net/ipv6/addrconf.c | |
parent | 5256f2ef3a40d784b8369035bff3f4dc637a9801 (diff) |
ipv6: speedup inet6_dump_addr()
When handling large number of netdevices, inet6_dump_addr()
is very slow because it has O(N^2) complexity.
Instead of scanning one single list, we can use the NETDEV_HASHENTRIES
sub lists of the dev_index hash table, and RCU lookups.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 171 |
1 files changed, 97 insertions, 74 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9ff8ab9a1549..522bdc77206c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -3481,91 +3481,114 @@ enum addr_type_t | |||
3481 | ANYCAST_ADDR, | 3481 | ANYCAST_ADDR, |
3482 | }; | 3482 | }; |
3483 | 3483 | ||
3484 | /* called with rcu_read_lock() */ | ||
3485 | static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, | ||
3486 | struct netlink_callback *cb, enum addr_type_t type, | ||
3487 | int s_ip_idx, int *p_ip_idx) | ||
3488 | { | ||
3489 | struct inet6_ifaddr *ifa; | ||
3490 | struct ifmcaddr6 *ifmca; | ||
3491 | struct ifacaddr6 *ifaca; | ||
3492 | int err = 1; | ||
3493 | int ip_idx = *p_ip_idx; | ||
3494 | |||
3495 | read_lock_bh(&idev->lock); | ||
3496 | switch (type) { | ||
3497 | case UNICAST_ADDR: | ||
3498 | /* unicast address incl. temp addr */ | ||
3499 | for (ifa = idev->addr_list; ifa; | ||
3500 | ifa = ifa->if_next, ip_idx++) { | ||
3501 | if (ip_idx < s_ip_idx) | ||
3502 | continue; | ||
3503 | err = inet6_fill_ifaddr(skb, ifa, | ||
3504 | NETLINK_CB(cb->skb).pid, | ||
3505 | cb->nlh->nlmsg_seq, | ||
3506 | RTM_NEWADDR, | ||
3507 | NLM_F_MULTI); | ||
3508 | if (err <= 0) | ||
3509 | break; | ||
3510 | } | ||
3511 | break; | ||
3512 | case MULTICAST_ADDR: | ||
3513 | /* multicast address */ | ||
3514 | for (ifmca = idev->mc_list; ifmca; | ||
3515 | ifmca = ifmca->next, ip_idx++) { | ||
3516 | if (ip_idx < s_ip_idx) | ||
3517 | continue; | ||
3518 | err = inet6_fill_ifmcaddr(skb, ifmca, | ||
3519 | NETLINK_CB(cb->skb).pid, | ||
3520 | cb->nlh->nlmsg_seq, | ||
3521 | RTM_GETMULTICAST, | ||
3522 | NLM_F_MULTI); | ||
3523 | if (err <= 0) | ||
3524 | break; | ||
3525 | } | ||
3526 | break; | ||
3527 | case ANYCAST_ADDR: | ||
3528 | /* anycast address */ | ||
3529 | for (ifaca = idev->ac_list; ifaca; | ||
3530 | ifaca = ifaca->aca_next, ip_idx++) { | ||
3531 | if (ip_idx < s_ip_idx) | ||
3532 | continue; | ||
3533 | err = inet6_fill_ifacaddr(skb, ifaca, | ||
3534 | NETLINK_CB(cb->skb).pid, | ||
3535 | cb->nlh->nlmsg_seq, | ||
3536 | RTM_GETANYCAST, | ||
3537 | NLM_F_MULTI); | ||
3538 | if (err <= 0) | ||
3539 | break; | ||
3540 | } | ||
3541 | break; | ||
3542 | default: | ||
3543 | break; | ||
3544 | } | ||
3545 | read_unlock_bh(&idev->lock); | ||
3546 | *p_ip_idx = ip_idx; | ||
3547 | return err; | ||
3548 | } | ||
3549 | |||
3484 | static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, | 3550 | static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, |
3485 | enum addr_type_t type) | 3551 | enum addr_type_t type) |
3486 | { | 3552 | { |
3553 | struct net *net = sock_net(skb->sk); | ||
3554 | int h, s_h; | ||
3487 | int idx, ip_idx; | 3555 | int idx, ip_idx; |
3488 | int s_idx, s_ip_idx; | 3556 | int s_idx, s_ip_idx; |
3489 | int err = 1; | ||
3490 | struct net_device *dev; | 3557 | struct net_device *dev; |
3491 | struct inet6_dev *idev = NULL; | 3558 | struct inet6_dev *idev; |
3492 | struct inet6_ifaddr *ifa; | 3559 | struct hlist_head *head; |
3493 | struct ifmcaddr6 *ifmca; | 3560 | struct hlist_node *node; |
3494 | struct ifacaddr6 *ifaca; | ||
3495 | struct net *net = sock_net(skb->sk); | ||
3496 | 3561 | ||
3497 | s_idx = cb->args[0]; | 3562 | s_h = cb->args[0]; |
3498 | s_ip_idx = ip_idx = cb->args[1]; | 3563 | s_idx = idx = cb->args[1]; |
3564 | s_ip_idx = ip_idx = cb->args[2]; | ||
3499 | 3565 | ||
3500 | idx = 0; | 3566 | rcu_read_lock(); |
3501 | for_each_netdev(net, dev) { | 3567 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
3502 | if (idx < s_idx) | 3568 | idx = 0; |
3503 | goto cont; | 3569 | head = &net->dev_index_head[h]; |
3504 | if (idx > s_idx) | 3570 | hlist_for_each_entry_rcu(dev, node, head, index_hlist) { |
3505 | s_ip_idx = 0; | 3571 | if (idx < s_idx) |
3506 | ip_idx = 0; | 3572 | goto cont; |
3507 | if ((idev = in6_dev_get(dev)) == NULL) | 3573 | if (idx > s_idx) |
3508 | goto cont; | 3574 | s_ip_idx = 0; |
3509 | read_lock_bh(&idev->lock); | 3575 | ip_idx = 0; |
3510 | switch (type) { | 3576 | if ((idev = __in6_dev_get(dev)) == NULL) |
3511 | case UNICAST_ADDR: | 3577 | goto cont; |
3512 | /* unicast address incl. temp addr */ | ||
3513 | for (ifa = idev->addr_list; ifa; | ||
3514 | ifa = ifa->if_next, ip_idx++) { | ||
3515 | if (ip_idx < s_ip_idx) | ||
3516 | continue; | ||
3517 | err = inet6_fill_ifaddr(skb, ifa, | ||
3518 | NETLINK_CB(cb->skb).pid, | ||
3519 | cb->nlh->nlmsg_seq, | ||
3520 | RTM_NEWADDR, | ||
3521 | NLM_F_MULTI); | ||
3522 | if (err <= 0) | ||
3523 | break; | ||
3524 | } | ||
3525 | break; | ||
3526 | case MULTICAST_ADDR: | ||
3527 | /* multicast address */ | ||
3528 | for (ifmca = idev->mc_list; ifmca; | ||
3529 | ifmca = ifmca->next, ip_idx++) { | ||
3530 | if (ip_idx < s_ip_idx) | ||
3531 | continue; | ||
3532 | err = inet6_fill_ifmcaddr(skb, ifmca, | ||
3533 | NETLINK_CB(cb->skb).pid, | ||
3534 | cb->nlh->nlmsg_seq, | ||
3535 | RTM_GETMULTICAST, | ||
3536 | NLM_F_MULTI); | ||
3537 | if (err <= 0) | ||
3538 | break; | ||
3539 | } | ||
3540 | break; | ||
3541 | case ANYCAST_ADDR: | ||
3542 | /* anycast address */ | ||
3543 | for (ifaca = idev->ac_list; ifaca; | ||
3544 | ifaca = ifaca->aca_next, ip_idx++) { | ||
3545 | if (ip_idx < s_ip_idx) | ||
3546 | continue; | ||
3547 | err = inet6_fill_ifacaddr(skb, ifaca, | ||
3548 | NETLINK_CB(cb->skb).pid, | ||
3549 | cb->nlh->nlmsg_seq, | ||
3550 | RTM_GETANYCAST, | ||
3551 | NLM_F_MULTI); | ||
3552 | if (err <= 0) | ||
3553 | break; | ||
3554 | } | ||
3555 | break; | ||
3556 | default: | ||
3557 | break; | ||
3558 | } | ||
3559 | read_unlock_bh(&idev->lock); | ||
3560 | in6_dev_put(idev); | ||
3561 | 3578 | ||
3562 | if (err <= 0) | 3579 | if (in6_dump_addrs(idev, skb, cb, type, |
3563 | break; | 3580 | s_ip_idx, &ip_idx) <= 0) |
3581 | goto done; | ||
3564 | cont: | 3582 | cont: |
3565 | idx++; | 3583 | idx++; |
3584 | } | ||
3566 | } | 3585 | } |
3567 | cb->args[0] = idx; | 3586 | done: |
3568 | cb->args[1] = ip_idx; | 3587 | rcu_read_unlock(); |
3588 | cb->args[0] = h; | ||
3589 | cb->args[1] = idx; | ||
3590 | cb->args[2] = ip_idx; | ||
3591 | |||
3569 | return skb->len; | 3592 | return skb->len; |
3570 | } | 3593 | } |
3571 | 3594 | ||