Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72c9528b authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

net: Introduce dev_get_by_name_rcu()



Some workloads hit dev_base_lock rwlock pretty hard.
We can use RCU lookups to avoid touching this rwlock
(and avoid touching netdevice refcount)

netdevices are already freed after a RCU grace period, so this patch
adds no penalty at device dismantle time.

However, it adds a synchronize_rcu() call in dev_change_name()

Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 68d8287c
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -1115,6 +1115,7 @@ extern void __dev_remove_pack(struct packet_type *pt);
extern struct net_device	*dev_get_by_flags(struct net *net, unsigned short flags,
extern struct net_device	*dev_get_by_flags(struct net *net, unsigned short flags,
						  unsigned short mask);
						  unsigned short mask);
extern struct net_device	*dev_get_by_name(struct net *net, const char *name);
extern struct net_device	*dev_get_by_name(struct net *net, const char *name);
extern struct net_device	*dev_get_by_name_rcu(struct net *net, const char *name);
extern struct net_device	*__dev_get_by_name(struct net *net, const char *name);
extern struct net_device	*__dev_get_by_name(struct net *net, const char *name);
extern int		dev_alloc_name(struct net_device *dev, const char *name);
extern int		dev_alloc_name(struct net_device *dev, const char *name);
extern int		dev_open(struct net_device *dev);
extern int		dev_open(struct net_device *dev);
+40 −9
Original line number Original line Diff line number Diff line
@@ -213,7 +213,7 @@ 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_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
	hlist_add_head_rcu(&dev->index_hlist,
	hlist_add_head_rcu(&dev->index_hlist,
			   dev_index_hash(net, dev->ifindex));
			   dev_index_hash(net, dev->ifindex));
	write_unlock_bh(&dev_base_lock);
	write_unlock_bh(&dev_base_lock);
@@ -230,7 +230,7 @@ static void unlist_netdevice(struct net_device *dev)
	/* Unlink dev from the device chain */
	/* Unlink dev from the device chain */
	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_rcu(&dev->name_hlist);
	hlist_del_rcu(&dev->index_hlist);
	hlist_del_rcu(&dev->index_hlist);
	write_unlock_bh(&dev_base_lock);
	write_unlock_bh(&dev_base_lock);
}
}
@@ -598,6 +598,32 @@ struct net_device *__dev_get_by_name(struct net *net, const char *name)
}
}
EXPORT_SYMBOL(__dev_get_by_name);
EXPORT_SYMBOL(__dev_get_by_name);


/**
 *	dev_get_by_name_rcu	- find a device by its name
 *	@net: the applicable net namespace
 *	@name: name to find
 *
 *	Find an interface by name.
 *	If the name is found a pointer to the device is returned.
 * 	If the name is not found then %NULL is returned.
 *	The reference counters are not incremented so the caller must be
 *	careful with locks. The caller must hold RCU lock.
 */

struct net_device *dev_get_by_name_rcu(struct net *net, const char *name)
{
	struct hlist_node *p;
	struct net_device *dev;
	struct hlist_head *head = dev_name_hash(net, name);

	hlist_for_each_entry_rcu(dev, p, head, name_hlist)
		if (!strncmp(dev->name, name, IFNAMSIZ))
			return dev;

	return NULL;
}
EXPORT_SYMBOL(dev_get_by_name_rcu);

/**
/**
 *	dev_get_by_name		- find a device by its name
 *	dev_get_by_name		- find a device by its name
 *	@net: the applicable net namespace
 *	@net: the applicable net namespace
@@ -614,11 +640,11 @@ struct net_device *dev_get_by_name(struct net *net, const char *name)
{
{
	struct net_device *dev;
	struct net_device *dev;


	read_lock(&dev_base_lock);
	rcu_read_lock();
	dev = __dev_get_by_name(net, name);
	dev = dev_get_by_name_rcu(net, name);
	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_name);
EXPORT_SYMBOL(dev_get_by_name);
@@ -960,7 +986,12 @@ int dev_change_name(struct net_device *dev, const char *newname)


	write_lock_bh(&dev_base_lock);
	write_lock_bh(&dev_base_lock);
	hlist_del(&dev->name_hlist);
	hlist_del(&dev->name_hlist);
	hlist_add_head(&dev->name_hlist, dev_name_hash(net, dev->name));
	write_unlock_bh(&dev_base_lock);

	synchronize_rcu();

	write_lock_bh(&dev_base_lock);
	hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
	write_unlock_bh(&dev_base_lock);
	write_unlock_bh(&dev_base_lock);


	ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
	ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
@@ -1062,9 +1093,9 @@ void dev_load(struct net *net, const char *name)
{
{
	struct net_device *dev;
	struct net_device *dev;


	read_lock(&dev_base_lock);
	rcu_read_lock();
	dev = __dev_get_by_name(net, name);
	dev = dev_get_by_name_rcu(net, name);
	read_unlock(&dev_base_lock);
	rcu_read_unlock();


	if (!dev && capable(CAP_NET_ADMIN))
	if (!dev && capable(CAP_NET_ADMIN))
		request_module("%s", name);
		request_module("%s", name);