Skip to content

Commit 4ec9031

Browse files
committed
netdev: add "ops compat locking" helpers
Add helpers to "lock a netdev in a backward-compatible way", which for ops-locked netdevs will mean take the instance lock. For drivers which haven't opted into the ops locking we'll take rtnl_lock. The scoped foreach is dropping and re-taking the lock for each device, even if prev and next are both under rtnl_lock. I hope that's fine since we expect that netdev nl to be mostly supported by modern drivers, and modern drivers should also opt into the instance locking. Note that these helpers are mostly needed for queue related state, because drivers modify queue config in their ops in a non-atomic way. Or differently put, queue changes don't have a clear-cut API like NAPI configuration. Any state that can should just use the instance lock directly, not the "compat" hacks. Reviewed-by: Joe Damato <[email protected]> Acked-by: Stanislav Fomichev <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 606048c commit 4ec9031

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

include/net/netdev_lock.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ netdev_ops_assert_locked_or_invisible(const struct net_device *dev)
6464
netdev_ops_assert_locked(dev);
6565
}
6666

67+
static inline void netdev_lock_ops_compat(struct net_device *dev)
68+
{
69+
if (netdev_need_ops_lock(dev))
70+
netdev_lock(dev);
71+
else
72+
rtnl_lock();
73+
}
74+
75+
static inline void netdev_unlock_ops_compat(struct net_device *dev)
76+
{
77+
if (netdev_need_ops_lock(dev))
78+
netdev_unlock(dev);
79+
else
80+
rtnl_unlock();
81+
}
82+
6783
static inline int netdev_lock_cmp_fn(const struct lockdep_map *a,
6884
const struct lockdep_map *b)
6985
{

net/core/dev.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,20 @@ struct net_device *__netdev_put_lock(struct net_device *dev, struct net *net)
10521052
return dev;
10531053
}
10541054

1055+
static struct net_device *
1056+
__netdev_put_lock_ops_compat(struct net_device *dev, struct net *net)
1057+
{
1058+
netdev_lock_ops_compat(dev);
1059+
if (dev->reg_state > NETREG_REGISTERED ||
1060+
dev->moving_ns || !net_eq(dev_net(dev), net)) {
1061+
netdev_unlock_ops_compat(dev);
1062+
dev_put(dev);
1063+
return NULL;
1064+
}
1065+
dev_put(dev);
1066+
return dev;
1067+
}
1068+
10551069
/**
10561070
* netdev_get_by_index_lock() - find a device by its ifindex
10571071
* @net: the applicable net namespace
@@ -1074,6 +1088,18 @@ struct net_device *netdev_get_by_index_lock(struct net *net, int ifindex)
10741088
return __netdev_put_lock(dev, net);
10751089
}
10761090

1091+
struct net_device *
1092+
netdev_get_by_index_lock_ops_compat(struct net *net, int ifindex)
1093+
{
1094+
struct net_device *dev;
1095+
1096+
dev = dev_get_by_index(net, ifindex);
1097+
if (!dev)
1098+
return NULL;
1099+
1100+
return __netdev_put_lock_ops_compat(dev, net);
1101+
}
1102+
10771103
struct net_device *
10781104
netdev_xa_find_lock(struct net *net, struct net_device *dev,
10791105
unsigned long *index)
@@ -1099,6 +1125,31 @@ netdev_xa_find_lock(struct net *net, struct net_device *dev,
10991125
} while (true);
11001126
}
11011127

1128+
struct net_device *
1129+
netdev_xa_find_lock_ops_compat(struct net *net, struct net_device *dev,
1130+
unsigned long *index)
1131+
{
1132+
if (dev)
1133+
netdev_unlock_ops_compat(dev);
1134+
1135+
do {
1136+
rcu_read_lock();
1137+
dev = xa_find(&net->dev_by_index, index, ULONG_MAX, XA_PRESENT);
1138+
if (!dev) {
1139+
rcu_read_unlock();
1140+
return NULL;
1141+
}
1142+
dev_hold(dev);
1143+
rcu_read_unlock();
1144+
1145+
dev = __netdev_put_lock_ops_compat(dev, net);
1146+
if (dev)
1147+
return dev;
1148+
1149+
(*index)++;
1150+
} while (true);
1151+
}
1152+
11021153
static DEFINE_SEQLOCK(netdev_rename_lock);
11031154

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

net/core/dev.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ DEFINE_FREE(netdev_unlock, struct net_device *, if (_T) netdev_unlock(_T));
4242
(var_name = netdev_xa_find_lock(net, var_name, &ifindex)); \
4343
ifindex++)
4444

45+
struct net_device *
46+
netdev_get_by_index_lock_ops_compat(struct net *net, int ifindex);
47+
struct net_device *
48+
netdev_xa_find_lock_ops_compat(struct net *net, struct net_device *dev,
49+
unsigned long *index);
50+
51+
DEFINE_FREE(netdev_unlock_ops_compat, struct net_device *,
52+
if (_T) netdev_unlock_ops_compat(_T));
53+
54+
#define for_each_netdev_lock_ops_compat_scoped(net, var_name, ifindex) \
55+
for (struct net_device *var_name __free(netdev_unlock_ops_compat) = NULL; \
56+
(var_name = netdev_xa_find_lock_ops_compat(net, var_name, \
57+
&ifindex)); \
58+
ifindex++)
59+
4560
#ifdef CONFIG_PROC_FS
4661
int __init dev_proc_init(void);
4762
#else

0 commit comments

Comments
 (0)