diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2009-10-19 15:18:49 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-10-29 04:42:55 -0400 |
commit | fb699dfd426a189fe33b91586c15176a75c8aed0 (patch) | |
tree | c90a8aadc10ac9a9e7b67b2d36ce61853917e3ba /net/core/dev.c | |
parent | e0c5567d06ecf7777b6c46f4d933a0a6e09a44f3 (diff) |
net: Introduce dev_get_by_index_rcu()
Some workloads hit dev_base_lock rwlock pretty hard.
We can use RCU lookups to avoid touching this rwlock.
netdevices are already freed after a RCU grace period, so this patch
adds no penalty at device dismantle time.
dev_ifname() converted to dev_get_by_index_rcu()
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 48 |
1 files changed, 38 insertions, 10 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 09551cc143a9..68a1bb68b5a8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -214,12 +214,15 @@ static int list_netdevice(struct net_device *dev) | |||
214 | write_lock_bh(&dev_base_lock); | 214 | write_lock_bh(&dev_base_lock); |
215 | list_add_tail(&dev->dev_list, &net->dev_base_head); | 215 | list_add_tail(&dev->dev_list, &net->dev_base_head); |
216 | hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name)); | 216 | hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name)); |
217 | hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex)); | 217 | hlist_add_head_rcu(&dev->index_hlist, |
218 | dev_index_hash(net, dev->ifindex)); | ||
218 | write_unlock_bh(&dev_base_lock); | 219 | write_unlock_bh(&dev_base_lock); |
219 | return 0; | 220 | return 0; |
220 | } | 221 | } |
221 | 222 | ||
222 | /* Device list removal */ | 223 | /* Device list removal |
224 | * caller must respect a RCU grace period before freeing/reusing dev | ||
225 | */ | ||
223 | static void unlist_netdevice(struct net_device *dev) | 226 | static void unlist_netdevice(struct net_device *dev) |
224 | { | 227 | { |
225 | ASSERT_RTNL(); | 228 | ASSERT_RTNL(); |
@@ -228,7 +231,7 @@ static void unlist_netdevice(struct net_device *dev) | |||
228 | write_lock_bh(&dev_base_lock); | 231 | write_lock_bh(&dev_base_lock); |
229 | list_del(&dev->dev_list); | 232 | list_del(&dev->dev_list); |
230 | hlist_del(&dev->name_hlist); | 233 | hlist_del(&dev->name_hlist); |
231 | hlist_del(&dev->index_hlist); | 234 | hlist_del_rcu(&dev->index_hlist); |
232 | write_unlock_bh(&dev_base_lock); | 235 | write_unlock_bh(&dev_base_lock); |
233 | } | 236 | } |
234 | 237 | ||
@@ -646,6 +649,31 @@ struct net_device *__dev_get_by_index(struct net *net, int ifindex) | |||
646 | } | 649 | } |
647 | EXPORT_SYMBOL(__dev_get_by_index); | 650 | EXPORT_SYMBOL(__dev_get_by_index); |
648 | 651 | ||
652 | /** | ||
653 | * dev_get_by_index_rcu - find a device by its ifindex | ||
654 | * @net: the applicable net namespace | ||
655 | * @ifindex: index of device | ||
656 | * | ||
657 | * Search for an interface by index. Returns %NULL if the device | ||
658 | * is not found or a pointer to the device. The device has not | ||
659 | * had its reference counter increased so the caller must be careful | ||
660 | * about locking. The caller must hold RCU lock. | ||
661 | */ | ||
662 | |||
663 | struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex) | ||
664 | { | ||
665 | struct hlist_node *p; | ||
666 | struct net_device *dev; | ||
667 | struct hlist_head *head = dev_index_hash(net, ifindex); | ||
668 | |||
669 | hlist_for_each_entry_rcu(dev, p, head, index_hlist) | ||
670 | if (dev->ifindex == ifindex) | ||
671 | return dev; | ||
672 | |||
673 | return NULL; | ||
674 | } | ||
675 | EXPORT_SYMBOL(dev_get_by_index_rcu); | ||
676 | |||
649 | 677 | ||
650 | /** | 678 | /** |
651 | * dev_get_by_index - find a device by its ifindex | 679 | * dev_get_by_index - find a device by its ifindex |
@@ -662,11 +690,11 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex) | |||
662 | { | 690 | { |
663 | struct net_device *dev; | 691 | struct net_device *dev; |
664 | 692 | ||
665 | read_lock(&dev_base_lock); | 693 | rcu_read_lock(); |
666 | dev = __dev_get_by_index(net, ifindex); | 694 | dev = dev_get_by_index_rcu(net, ifindex); |
667 | if (dev) | 695 | if (dev) |
668 | dev_hold(dev); | 696 | dev_hold(dev); |
669 | read_unlock(&dev_base_lock); | 697 | rcu_read_unlock(); |
670 | return dev; | 698 | return dev; |
671 | } | 699 | } |
672 | EXPORT_SYMBOL(dev_get_by_index); | 700 | EXPORT_SYMBOL(dev_get_by_index); |
@@ -2939,15 +2967,15 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg) | |||
2939 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | 2967 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) |
2940 | return -EFAULT; | 2968 | return -EFAULT; |
2941 | 2969 | ||
2942 | read_lock(&dev_base_lock); | 2970 | rcu_read_lock(); |
2943 | dev = __dev_get_by_index(net, ifr.ifr_ifindex); | 2971 | dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); |
2944 | if (!dev) { | 2972 | if (!dev) { |
2945 | read_unlock(&dev_base_lock); | 2973 | rcu_read_unlock(); |
2946 | return -ENODEV; | 2974 | return -ENODEV; |
2947 | } | 2975 | } |
2948 | 2976 | ||
2949 | strcpy(ifr.ifr_name, dev->name); | 2977 | strcpy(ifr.ifr_name, dev->name); |
2950 | read_unlock(&dev_base_lock); | 2978 | rcu_read_unlock(); |
2951 | 2979 | ||
2952 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) | 2980 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) |
2953 | return -EFAULT; | 2981 | return -EFAULT; |