Commit 473f5e13 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'netdev-tracking'

Jakub Kicinski says:

====================
net: create device lookup API with reference tracking

We still see dev_hold() / dev_put() calls without reference tracker
getting added in new code. dev_get_by_name() / dev_get_by_index()
seem to be one of the sources of those. Provide appropriate helpers.
Allocating the tracker can obviously be done with an additional call
to netdev_tracker_alloc(), but a single API feels cleaner.

v2:
 - fix a dev_put() in ethtool
v1: https://lore.kernel.org/all/20230609183207.1466075-1-kuba@kernel.org/


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 89da780a 48eed027
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3124,6 +3124,10 @@ struct net_device *netdev_sk_get_lowest_dev(struct net_device *dev,
					    struct sock *sk);
struct net_device *dev_get_by_index(struct net *net, int ifindex);
struct net_device *__dev_get_by_index(struct net *net, int ifindex);
struct net_device *netdev_get_by_index(struct net *net, int ifindex,
				       netdevice_tracker *tracker, gfp_t gfp);
struct net_device *netdev_get_by_name(struct net *net, const char *name,
				      netdevice_tracker *tracker, gfp_t gfp);
struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
struct net_device *dev_get_by_napi_id(unsigned int napi_id);
int dev_restart(struct net_device *dev);
+45 −18
Original line number Diff line number Diff line
@@ -758,29 +758,43 @@ struct net_device *dev_get_by_name_rcu(struct net *net, const char *name)
}
EXPORT_SYMBOL(dev_get_by_name_rcu);

/* Deprecated for new users, call netdev_get_by_name() instead */
struct net_device *dev_get_by_name(struct net *net, const char *name)
{
	struct net_device *dev;

	rcu_read_lock();
	dev = dev_get_by_name_rcu(net, name);
	dev_hold(dev);
	rcu_read_unlock();
	return dev;
}
EXPORT_SYMBOL(dev_get_by_name);

/**
 *	dev_get_by_name		- find a device by its name
 *	netdev_get_by_name() - find a device by its name
 *	@net: the applicable net namespace
 *	@name: name to find
 *	@tracker: tracking object for the acquired reference
 *	@gfp: allocation flags for the tracker
 *
 *	Find an interface by name. This can be called from any
 *	context and does its own locking. The returned handle has
 *	the usage count incremented and the caller must use dev_put() to
 *	the usage count incremented and the caller must use netdev_put() to
 *	release it when it is no longer needed. %NULL is returned if no
 *	matching device is found.
 */

struct net_device *dev_get_by_name(struct net *net, const char *name)
struct net_device *netdev_get_by_name(struct net *net, const char *name,
				      netdevice_tracker *tracker, gfp_t gfp)
{
	struct net_device *dev;

	rcu_read_lock();
	dev = dev_get_by_name_rcu(net, name);
	dev_hold(dev);
	rcu_read_unlock();
	dev = dev_get_by_name(net, name);
	if (dev)
		netdev_tracker_alloc(dev, tracker, gfp);
	return dev;
}
EXPORT_SYMBOL(dev_get_by_name);
EXPORT_SYMBOL(netdev_get_by_name);

/**
 *	__dev_get_by_index - find a device by its ifindex
@@ -831,29 +845,42 @@ struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex)
}
EXPORT_SYMBOL(dev_get_by_index_rcu);

/* Deprecated for new users, call netdev_get_by_index() instead */
struct net_device *dev_get_by_index(struct net *net, int ifindex)
{
	struct net_device *dev;

	rcu_read_lock();
	dev = dev_get_by_index_rcu(net, ifindex);
	dev_hold(dev);
	rcu_read_unlock();
	return dev;
}
EXPORT_SYMBOL(dev_get_by_index);

/**
 *	dev_get_by_index - find a device by its ifindex
 *	netdev_get_by_index() - find a device by its ifindex
 *	@net: the applicable net namespace
 *	@ifindex: index of device
 *	@tracker: tracking object for the acquired reference
 *	@gfp: allocation flags for the tracker
 *
 *	Search for an interface by index. Returns NULL if the device
 *	is not found or a pointer to the device. The device returned has
 *	had a reference added and the pointer is safe until the user calls
 *	dev_put to indicate they have finished with it.
 *	netdev_put() to indicate they have finished with it.
 */

struct net_device *dev_get_by_index(struct net *net, int ifindex)
struct net_device *netdev_get_by_index(struct net *net, int ifindex,
				       netdevice_tracker *tracker, gfp_t gfp)
{
	struct net_device *dev;

	rcu_read_lock();
	dev = dev_get_by_index_rcu(net, ifindex);
	dev_hold(dev);
	rcu_read_unlock();
	dev = dev_get_by_index(net, ifindex);
	if (dev)
		netdev_tracker_alloc(dev, tracker, gfp);
	return dev;
}
EXPORT_SYMBOL(dev_get_by_index);
EXPORT_SYMBOL(netdev_get_by_index);

/**
 *	dev_get_by_napi_id - find a device by napi_id
+2 −3
Original line number Diff line number Diff line
@@ -690,7 +690,7 @@ int netpoll_setup(struct netpoll *np)
		err = -ENODEV;
		goto unlock;
	}
	dev_hold(ndev);
	netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL);

	if (netdev_master_upper_dev_get(ndev)) {
		np_err(np, "%s is a slave device, aborting\n", np->dev_name);
@@ -783,12 +783,11 @@ int netpoll_setup(struct netpoll *np)
	err = __netpoll_setup(np, ndev);
	if (err)
		goto put;
	netdev_tracker_alloc(ndev, &np->dev_tracker, GFP_KERNEL);
	rtnl_unlock();
	return 0;

put:
	dev_put(ndev);
	netdev_put(ndev, &np->dev_tracker);
unlock:
	rtnl_unlock();
	return err;
+5 −5
Original line number Diff line number Diff line
@@ -115,7 +115,8 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
	if (tb[ETHTOOL_A_HEADER_DEV_INDEX]) {
		u32 ifindex = nla_get_u32(tb[ETHTOOL_A_HEADER_DEV_INDEX]);

		dev = dev_get_by_index(net, ifindex);
		dev = netdev_get_by_index(net, ifindex, &req_info->dev_tracker,
					  GFP_KERNEL);
		if (!dev) {
			NL_SET_ERR_MSG_ATTR(extack,
					    tb[ETHTOOL_A_HEADER_DEV_INDEX],
@@ -125,13 +126,14 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
		/* if both ifindex and ifname are passed, they must match */
		if (devname_attr &&
		    strncmp(dev->name, nla_data(devname_attr), IFNAMSIZ)) {
			dev_put(dev);
			netdev_put(dev, &req_info->dev_tracker);
			NL_SET_ERR_MSG_ATTR(extack, header,
					    "ifindex and name do not match");
			return -ENODEV;
		}
	} else if (devname_attr) {
		dev = dev_get_by_name(net, nla_data(devname_attr));
		dev = netdev_get_by_name(net, nla_data(devname_attr),
					 &req_info->dev_tracker, GFP_KERNEL);
		if (!dev) {
			NL_SET_ERR_MSG_ATTR(extack, devname_attr,
					    "no device matches name");
@@ -144,8 +146,6 @@ int ethnl_parse_header_dev_get(struct ethnl_req_info *req_info,
	}

	req_info->dev = dev;
	if (dev)
		netdev_tracker_alloc(dev, &req_info->dev_tracker, GFP_KERNEL);
	req_info->flags = flags;
	return 0;
}
+6 −6
Original line number Diff line number Diff line
@@ -3503,6 +3503,7 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
		 struct fib6_config *cfg, gfp_t gfp_flags,
		 struct netlink_ext_ack *extack)
{
	netdevice_tracker *dev_tracker = &fib6_nh->fib_nh_dev_tracker;
	struct net_device *dev = NULL;
	struct inet6_dev *idev = NULL;
	int addr_type;
@@ -3520,7 +3521,8 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,

	err = -ENODEV;
	if (cfg->fc_ifindex) {
		dev = dev_get_by_index(net, cfg->fc_ifindex);
		dev = netdev_get_by_index(net, cfg->fc_ifindex,
					  dev_tracker, gfp_flags);
		if (!dev)
			goto out;
		idev = in6_dev_get(dev);
@@ -3554,11 +3556,11 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
		/* hold loopback dev/idev if we haven't done so. */
		if (dev != net->loopback_dev) {
			if (dev) {
				dev_put(dev);
				netdev_put(dev, dev_tracker);
				in6_dev_put(idev);
			}
			dev = net->loopback_dev;
			dev_hold(dev);
			netdev_hold(dev, dev_tracker, gfp_flags);
			idev = in6_dev_get(dev);
			if (!idev) {
				err = -ENODEV;
@@ -3610,8 +3612,6 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
	}

	fib6_nh->fib_nh_dev = dev;
	netdev_tracker_alloc(dev, &fib6_nh->fib_nh_dev_tracker, gfp_flags);

	fib6_nh->fib_nh_oif = dev->ifindex;
	err = 0;
out:
@@ -3621,7 +3621,7 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
	if (err) {
		lwtstate_put(fib6_nh->fib_nh_lws);
		fib6_nh->fib_nh_lws = NULL;
		dev_put(dev);
		netdev_put(dev, dev_tracker);
	}

	return err;