aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2009-11-11 23:11:50 -0500
committerDavid S. Miller <davem@davemloft.net>2009-11-13 23:46:57 -0500
commit234b27c3fd58fc0e15c04dd0fbf4337fac9c2a06 (patch)
treeb7cc69200bee85872e2b8bf7fa93d2c8b519de2e /net/ipv6/addrconf.c
parent5256f2ef3a40d784b8369035bff3f4dc637a9801 (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.c171
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() */
3485static 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
3484static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, 3550static 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;
3564cont: 3582cont:
3565 idx++; 3583 idx++;
3584 }
3566 } 3585 }
3567 cb->args[0] = idx; 3586done:
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