Skip to content

Commit

Permalink
net: add helpers for lookup and walking netdevs under netdev_lock()
Browse files Browse the repository at this point in the history
Add helpers for accessing netdevs under netdev_lock().
There's some careful handling needed to find the device and lock it
safely, without it getting unregistered, and without taking rtnl_lock
(the latter being the whole point of the new locking, after all).

Reviewed-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250115035319.559603-4-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Jan 16, 2025
1 parent 5fda3f3 commit 2628f49
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
110 changes: 110 additions & 0 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,49 @@ struct napi_struct *netdev_napi_by_id(struct net *net, unsigned int napi_id)
return napi;
}

/**
* netdev_napi_by_id_lock() - find a device by NAPI ID and lock it
* @net: the applicable net namespace
* @napi_id: ID of a NAPI of a target device
*
* Find a NAPI instance with @napi_id. Lock its device.
* The device must be in %NETREG_REGISTERED state for lookup to succeed.
* netdev_unlock() must be called to release it.
*
* Return: pointer to NAPI, its device with lock held, NULL if not found.
*/
struct napi_struct *
netdev_napi_by_id_lock(struct net *net, unsigned int napi_id)
{
struct napi_struct *napi;
struct net_device *dev;

rcu_read_lock();
napi = netdev_napi_by_id(net, napi_id);
if (!napi || READ_ONCE(napi->dev->reg_state) != NETREG_REGISTERED) {
rcu_read_unlock();
return NULL;
}

dev = napi->dev;
dev_hold(dev);
rcu_read_unlock();

dev = __netdev_put_lock(dev);
if (!dev)
return NULL;

rcu_read_lock();
napi = netdev_napi_by_id(net, napi_id);
if (napi && napi->dev != dev)
napi = NULL;
rcu_read_unlock();

if (!napi)
netdev_unlock(dev);
return napi;
}

/**
* __dev_get_by_name - find a device by its name
* @net: the applicable net namespace
Expand Down Expand Up @@ -972,6 +1015,73 @@ struct net_device *dev_get_by_napi_id(unsigned int napi_id)
return napi ? napi->dev : NULL;
}

/* Release the held reference on the net_device, and if the net_device
* is still registered try to lock the instance lock. If device is being
* unregistered NULL will be returned (but the reference has been released,
* either way!)
*
* This helper is intended for locking net_device after it has been looked up
* using a lockless lookup helper. Lock prevents the instance from going away.
*/
struct net_device *__netdev_put_lock(struct net_device *dev)
{
netdev_lock(dev);
if (dev->reg_state > NETREG_REGISTERED) {
netdev_unlock(dev);
dev_put(dev);
return NULL;
}
dev_put(dev);
return dev;
}

/**
* netdev_get_by_index_lock() - find a device by its ifindex
* @net: the applicable net namespace
* @ifindex: index of device
*
* Search for an interface by index. If a valid device
* with @ifindex is found it will be returned with netdev->lock held.
* netdev_unlock() must be called to release it.
*
* Return: pointer to a device with lock held, NULL if not found.
*/
struct net_device *netdev_get_by_index_lock(struct net *net, int ifindex)
{
struct net_device *dev;

dev = dev_get_by_index(net, ifindex);
if (!dev)
return NULL;

return __netdev_put_lock(dev);
}

struct net_device *
netdev_xa_find_lock(struct net *net, struct net_device *dev,
unsigned long *index)
{
if (dev)
netdev_unlock(dev);

do {
rcu_read_lock();
dev = xa_find(&net->dev_by_index, index, ULONG_MAX, XA_PRESENT);
if (!dev) {
rcu_read_unlock();
return NULL;
}
dev_hold(dev);
rcu_read_unlock();

dev = __netdev_put_lock(dev);
if (dev)
return dev;

(*index)++;
} while (true);
}

static DEFINE_SEQLOCK(netdev_rename_lock);

void netdev_copy_name(struct net_device *dev, char *name)
Expand Down
16 changes: 16 additions & 0 deletions net/core/dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#ifndef _NET_CORE_DEV_H
#define _NET_CORE_DEV_H

#include <linux/cleanup.h>
#include <linux/types.h>
#include <linux/rwsem.h>
#include <linux/netdevice.h>
Expand All @@ -23,8 +24,23 @@ struct sd_flow_limit {
extern int netdev_flow_limit_table_len;

struct napi_struct *netdev_napi_by_id(struct net *net, unsigned int napi_id);
struct napi_struct *
netdev_napi_by_id_lock(struct net *net, unsigned int napi_id);
struct net_device *dev_get_by_napi_id(unsigned int napi_id);

struct net_device *netdev_get_by_index_lock(struct net *net, int ifindex);
struct net_device *__netdev_put_lock(struct net_device *dev);
struct net_device *
netdev_xa_find_lock(struct net *net, struct net_device *dev,
unsigned long *index);

DEFINE_FREE(netdev_unlock, struct net_device *, if (_T) netdev_unlock(_T));

#define for_each_netdev_lock_scoped(net, var_name, ifindex) \
for (struct net_device *var_name __free(netdev_unlock) = NULL; \
(var_name = netdev_xa_find_lock(net, var_name, &ifindex)); \
ifindex++)

#ifdef CONFIG_PROC_FS
int __init dev_proc_init(void);
#else
Expand Down

0 comments on commit 2628f49

Please sign in to comment.