aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2009-10-19 15:18:49 -0400
committerDavid S. Miller <davem@davemloft.net>2009-10-29 04:42:55 -0400
commitfb699dfd426a189fe33b91586c15176a75c8aed0 (patch)
treec90a8aadc10ac9a9e7b67b2d36ce61853917e3ba
parente0c5567d06ecf7777b6c46f4d933a0a6e09a44f3 (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>
-rw-r--r--include/linux/netdevice.h1
-rw-r--r--net/core/dev.c48
2 files changed, 39 insertions, 10 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 656110a46e96..ffc3106cc037 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1139,6 +1139,7 @@ extern void netdev_resync_ops(struct net_device *dev);
1139extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev); 1139extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev);
1140extern struct net_device *dev_get_by_index(struct net *net, int ifindex); 1140extern struct net_device *dev_get_by_index(struct net *net, int ifindex);
1141extern struct net_device *__dev_get_by_index(struct net *net, int ifindex); 1141extern struct net_device *__dev_get_by_index(struct net *net, int ifindex);
1142extern struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
1142extern int dev_restart(struct net_device *dev); 1143extern int dev_restart(struct net_device *dev);
1143#ifdef CONFIG_NETPOLL_TRAP 1144#ifdef CONFIG_NETPOLL_TRAP
1144extern int netpoll_trap(void); 1145extern int netpoll_trap(void);
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 */
223static void unlist_netdevice(struct net_device *dev) 226static 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}
647EXPORT_SYMBOL(__dev_get_by_index); 650EXPORT_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
663struct 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}
675EXPORT_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}
672EXPORT_SYMBOL(dev_get_by_index); 700EXPORT_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;