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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2023-06-15 08:21:12 +01:00
commit 473f5e13b3
5 changed files with 68 additions and 38 deletions

View file

@ -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);

View file

@ -758,18 +758,7 @@ struct net_device *dev_get_by_name_rcu(struct net *net, const char *name)
}
EXPORT_SYMBOL(dev_get_by_name_rcu);
/**
* dev_get_by_name - find a device by its name
* @net: the applicable net namespace
* @name: name to find
*
* 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
* release it when it is no longer needed. %NULL is returned if no
* matching device is found.
*/
/* 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;
@ -782,6 +771,31 @@ struct net_device *dev_get_by_name(struct net *net, const char *name)
}
EXPORT_SYMBOL(dev_get_by_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 netdev_put() to
* release it when it is no longer needed. %NULL is returned if no
* matching device is found.
*/
struct net_device *netdev_get_by_name(struct net *net, const char *name,
netdevice_tracker *tracker, gfp_t gfp)
{
struct net_device *dev;
dev = dev_get_by_name(net, name);
if (dev)
netdev_tracker_alloc(dev, tracker, gfp);
return dev;
}
EXPORT_SYMBOL(netdev_get_by_name);
/**
* __dev_get_by_index - find a device by its ifindex
* @net: the applicable net namespace
@ -831,18 +845,7 @@ struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex)
}
EXPORT_SYMBOL(dev_get_by_index_rcu);
/**
* dev_get_by_index - 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 returned has
* had a reference added and the pointer is safe until the user calls
* dev_put to indicate they have finished with it.
*/
/* 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;
@ -855,6 +858,30 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex)
}
EXPORT_SYMBOL(dev_get_by_index);
/**
* 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
* netdev_put() to indicate they have finished with it.
*/
struct net_device *netdev_get_by_index(struct net *net, int ifindex,
netdevice_tracker *tracker, gfp_t gfp)
{
struct net_device *dev;
dev = dev_get_by_index(net, ifindex);
if (dev)
netdev_tracker_alloc(dev, tracker, gfp);
return dev;
}
EXPORT_SYMBOL(netdev_get_by_index);
/**
* dev_get_by_napi_id - find a device by napi_id
* @napi_id: ID of the NAPI struct

View file

@ -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;

View file

@ -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;
}

View file

@ -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;