Skip to content

Commit 2628f49

Browse files
committed
net: add helpers for lookup and walking netdevs under netdev_lock()
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 <[email protected]> Reviewed-by: Kuniyuki Iwashima <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 5fda3f3 commit 2628f49

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

net/core/dev.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,49 @@ struct napi_struct *netdev_napi_by_id(struct net *net, unsigned int napi_id)
784784
return napi;
785785
}
786786

787+
/**
788+
* netdev_napi_by_id_lock() - find a device by NAPI ID and lock it
789+
* @net: the applicable net namespace
790+
* @napi_id: ID of a NAPI of a target device
791+
*
792+
* Find a NAPI instance with @napi_id. Lock its device.
793+
* The device must be in %NETREG_REGISTERED state for lookup to succeed.
794+
* netdev_unlock() must be called to release it.
795+
*
796+
* Return: pointer to NAPI, its device with lock held, NULL if not found.
797+
*/
798+
struct napi_struct *
799+
netdev_napi_by_id_lock(struct net *net, unsigned int napi_id)
800+
{
801+
struct napi_struct *napi;
802+
struct net_device *dev;
803+
804+
rcu_read_lock();
805+
napi = netdev_napi_by_id(net, napi_id);
806+
if (!napi || READ_ONCE(napi->dev->reg_state) != NETREG_REGISTERED) {
807+
rcu_read_unlock();
808+
return NULL;
809+
}
810+
811+
dev = napi->dev;
812+
dev_hold(dev);
813+
rcu_read_unlock();
814+
815+
dev = __netdev_put_lock(dev);
816+
if (!dev)
817+
return NULL;
818+
819+
rcu_read_lock();
820+
napi = netdev_napi_by_id(net, napi_id);
821+
if (napi && napi->dev != dev)
822+
napi = NULL;
823+
rcu_read_unlock();
824+
825+
if (!napi)
826+
netdev_unlock(dev);
827+
return napi;
828+
}
829+
787830
/**
788831
* __dev_get_by_name - find a device by its name
789832
* @net: the applicable net namespace
@@ -972,6 +1015,73 @@ struct net_device *dev_get_by_napi_id(unsigned int napi_id)
9721015
return napi ? napi->dev : NULL;
9731016
}
9741017

1018+
/* Release the held reference on the net_device, and if the net_device
1019+
* is still registered try to lock the instance lock. If device is being
1020+
* unregistered NULL will be returned (but the reference has been released,
1021+
* either way!)
1022+
*
1023+
* This helper is intended for locking net_device after it has been looked up
1024+
* using a lockless lookup helper. Lock prevents the instance from going away.
1025+
*/
1026+
struct net_device *__netdev_put_lock(struct net_device *dev)
1027+
{
1028+
netdev_lock(dev);
1029+
if (dev->reg_state > NETREG_REGISTERED) {
1030+
netdev_unlock(dev);
1031+
dev_put(dev);
1032+
return NULL;
1033+
}
1034+
dev_put(dev);
1035+
return dev;
1036+
}
1037+
1038+
/**
1039+
* netdev_get_by_index_lock() - find a device by its ifindex
1040+
* @net: the applicable net namespace
1041+
* @ifindex: index of device
1042+
*
1043+
* Search for an interface by index. If a valid device
1044+
* with @ifindex is found it will be returned with netdev->lock held.
1045+
* netdev_unlock() must be called to release it.
1046+
*
1047+
* Return: pointer to a device with lock held, NULL if not found.
1048+
*/
1049+
struct net_device *netdev_get_by_index_lock(struct net *net, int ifindex)
1050+
{
1051+
struct net_device *dev;
1052+
1053+
dev = dev_get_by_index(net, ifindex);
1054+
if (!dev)
1055+
return NULL;
1056+
1057+
return __netdev_put_lock(dev);
1058+
}
1059+
1060+
struct net_device *
1061+
netdev_xa_find_lock(struct net *net, struct net_device *dev,
1062+
unsigned long *index)
1063+
{
1064+
if (dev)
1065+
netdev_unlock(dev);
1066+
1067+
do {
1068+
rcu_read_lock();
1069+
dev = xa_find(&net->dev_by_index, index, ULONG_MAX, XA_PRESENT);
1070+
if (!dev) {
1071+
rcu_read_unlock();
1072+
return NULL;
1073+
}
1074+
dev_hold(dev);
1075+
rcu_read_unlock();
1076+
1077+
dev = __netdev_put_lock(dev);
1078+
if (dev)
1079+
return dev;
1080+
1081+
(*index)++;
1082+
} while (true);
1083+
}
1084+
9751085
static DEFINE_SEQLOCK(netdev_rename_lock);
9761086

9771087
void netdev_copy_name(struct net_device *dev, char *name)

net/core/dev.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#ifndef _NET_CORE_DEV_H
33
#define _NET_CORE_DEV_H
44

5+
#include <linux/cleanup.h>
56
#include <linux/types.h>
67
#include <linux/rwsem.h>
78
#include <linux/netdevice.h>
@@ -23,8 +24,23 @@ struct sd_flow_limit {
2324
extern int netdev_flow_limit_table_len;
2425

2526
struct napi_struct *netdev_napi_by_id(struct net *net, unsigned int napi_id);
27+
struct napi_struct *
28+
netdev_napi_by_id_lock(struct net *net, unsigned int napi_id);
2629
struct net_device *dev_get_by_napi_id(unsigned int napi_id);
2730

31+
struct net_device *netdev_get_by_index_lock(struct net *net, int ifindex);
32+
struct net_device *__netdev_put_lock(struct net_device *dev);
33+
struct net_device *
34+
netdev_xa_find_lock(struct net *net, struct net_device *dev,
35+
unsigned long *index);
36+
37+
DEFINE_FREE(netdev_unlock, struct net_device *, if (_T) netdev_unlock(_T));
38+
39+
#define for_each_netdev_lock_scoped(net, var_name, ifindex) \
40+
for (struct net_device *var_name __free(netdev_unlock) = NULL; \
41+
(var_name = netdev_xa_find_lock(net, var_name, &ifindex)); \
42+
ifindex++)
43+
2844
#ifdef CONFIG_PROC_FS
2945
int __init dev_proc_init(void);
3046
#else

0 commit comments

Comments
 (0)