|
@@ -214,12 +214,15 @@ static int list_netdevice(struct net_device *dev)
|
|
write_lock_bh(&dev_base_lock);
|
|
write_lock_bh(&dev_base_lock);
|
|
list_add_tail(&dev->dev_list, &net->dev_base_head);
|
|
list_add_tail(&dev->dev_list, &net->dev_base_head);
|
|
hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name));
|
|
hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name));
|
|
- hlist_add_head(&dev->index_hlist, dev_index_hash(net, dev->ifindex));
|
|
|
|
|
|
+ hlist_add_head_rcu(&dev->index_hlist,
|
|
|
|
+ dev_index_hash(net, dev->ifindex));
|
|
write_unlock_bh(&dev_base_lock);
|
|
write_unlock_bh(&dev_base_lock);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Device list removal */
|
|
|
|
|
|
+/* Device list removal
|
|
|
|
+ * caller must respect a RCU grace period before freeing/reusing dev
|
|
|
|
+ */
|
|
static void unlist_netdevice(struct net_device *dev)
|
|
static void unlist_netdevice(struct net_device *dev)
|
|
{
|
|
{
|
|
ASSERT_RTNL();
|
|
ASSERT_RTNL();
|
|
@@ -228,7 +231,7 @@ static void unlist_netdevice(struct net_device *dev)
|
|
write_lock_bh(&dev_base_lock);
|
|
write_lock_bh(&dev_base_lock);
|
|
list_del(&dev->dev_list);
|
|
list_del(&dev->dev_list);
|
|
hlist_del(&dev->name_hlist);
|
|
hlist_del(&dev->name_hlist);
|
|
- hlist_del(&dev->index_hlist);
|
|
|
|
|
|
+ hlist_del_rcu(&dev->index_hlist);
|
|
write_unlock_bh(&dev_base_lock);
|
|
write_unlock_bh(&dev_base_lock);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -646,6 +649,31 @@ struct net_device *__dev_get_by_index(struct net *net, int ifindex)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(__dev_get_by_index);
|
|
EXPORT_SYMBOL(__dev_get_by_index);
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * dev_get_by_index_rcu - find a device by its ifindex
|
|
|
|
+ * @net: the applicable net namespace
|
|
|
|
+ * @ifindex: index of device
|
|
|
|
+ *
|
|
|
|
+ * Search for an interface by index. Returns %NULL if the device
|
|
|
|
+ * is not found or a pointer to the device. The device has not
|
|
|
|
+ * had its reference counter increased so the caller must be careful
|
|
|
|
+ * about locking. The caller must hold RCU lock.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex)
|
|
|
|
+{
|
|
|
|
+ struct hlist_node *p;
|
|
|
|
+ struct net_device *dev;
|
|
|
|
+ struct hlist_head *head = dev_index_hash(net, ifindex);
|
|
|
|
+
|
|
|
|
+ hlist_for_each_entry_rcu(dev, p, head, index_hlist)
|
|
|
|
+ if (dev->ifindex == ifindex)
|
|
|
|
+ return dev;
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(dev_get_by_index_rcu);
|
|
|
|
+
|
|
|
|
|
|
/**
|
|
/**
|
|
* dev_get_by_index - find a device by its ifindex
|
|
* 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)
|
|
{
|
|
{
|
|
struct net_device *dev;
|
|
struct net_device *dev;
|
|
|
|
|
|
- read_lock(&dev_base_lock);
|
|
|
|
- dev = __dev_get_by_index(net, ifindex);
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ dev = dev_get_by_index_rcu(net, ifindex);
|
|
if (dev)
|
|
if (dev)
|
|
dev_hold(dev);
|
|
dev_hold(dev);
|
|
- read_unlock(&dev_base_lock);
|
|
|
|
|
|
+ rcu_read_unlock();
|
|
return dev;
|
|
return dev;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(dev_get_by_index);
|
|
EXPORT_SYMBOL(dev_get_by_index);
|
|
@@ -2939,15 +2967,15 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
|
|
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
|
|
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
|
|
return -EFAULT;
|
|
return -EFAULT;
|
|
|
|
|
|
- read_lock(&dev_base_lock);
|
|
|
|
- dev = __dev_get_by_index(net, ifr.ifr_ifindex);
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex);
|
|
if (!dev) {
|
|
if (!dev) {
|
|
- read_unlock(&dev_base_lock);
|
|
|
|
|
|
+ rcu_read_unlock();
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
|
|
strcpy(ifr.ifr_name, dev->name);
|
|
strcpy(ifr.ifr_name, dev->name);
|
|
- read_unlock(&dev_base_lock);
|
|
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
|
|
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
|
|
return -EFAULT;
|
|
return -EFAULT;
|