Skip to content

Commit 200b916

Browse files
congwang1986davem330
authored andcommitted
rtnetlink: wait for unregistering devices in rtnl_link_unregister()
From: Cong Wang <[email protected]> commit 50624c9 (net: Delay default_device_exit_batch until no devices are unregistering) introduced rtnl_lock_unregistering() for default_device_exit_batch(). Same race could happen we when rmmod a driver which calls rtnl_link_unregister() as we call dev->destructor without rtnl lock. For long term, I think we should clean up the mess of netdev_run_todo() and net namespce exit code. Cc: Eric W. Biederman <[email protected]> Cc: David S. Miller <[email protected]> Signed-off-by: Cong Wang <[email protected]> Signed-off-by: Cong Wang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent e84d2f8 commit 200b916

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

include/linux/rtnetlink.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <linux/mutex.h>
66
#include <linux/netdevice.h>
7+
#include <linux/wait.h>
78
#include <uapi/linux/rtnetlink.h>
89

910
extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo);
@@ -22,6 +23,10 @@ extern void rtnl_lock(void);
2223
extern void rtnl_unlock(void);
2324
extern int rtnl_trylock(void);
2425
extern int rtnl_is_locked(void);
26+
27+
extern wait_queue_head_t netdev_unregistering_wq;
28+
extern struct mutex net_mutex;
29+
2530
#ifdef CONFIG_PROVE_LOCKING
2631
extern int lockdep_rtnl_is_held(void);
2732
#else

net/core/dev.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5541,7 +5541,7 @@ static int dev_new_index(struct net *net)
55415541

55425542
/* Delayed registration/unregisteration */
55435543
static LIST_HEAD(net_todo_list);
5544-
static DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq);
5544+
DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq);
55455545

55465546
static void net_set_todo(struct net_device *dev)
55475547
{

net/core/net_namespace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
static LIST_HEAD(pernet_list);
2626
static struct list_head *first_device = &pernet_list;
27-
static DEFINE_MUTEX(net_mutex);
27+
DEFINE_MUTEX(net_mutex);
2828

2929
LIST_HEAD(net_namespace_list);
3030
EXPORT_SYMBOL_GPL(net_namespace_list);

net/core/rtnetlink.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,46 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops)
353353
}
354354
EXPORT_SYMBOL_GPL(__rtnl_link_unregister);
355355

356+
/* Return with the rtnl_lock held when there are no network
357+
* devices unregistering in any network namespace.
358+
*/
359+
static void rtnl_lock_unregistering_all(void)
360+
{
361+
struct net *net;
362+
bool unregistering;
363+
DEFINE_WAIT(wait);
364+
365+
for (;;) {
366+
prepare_to_wait(&netdev_unregistering_wq, &wait,
367+
TASK_UNINTERRUPTIBLE);
368+
unregistering = false;
369+
rtnl_lock();
370+
for_each_net(net) {
371+
if (net->dev_unreg_count > 0) {
372+
unregistering = true;
373+
break;
374+
}
375+
}
376+
if (!unregistering)
377+
break;
378+
__rtnl_unlock();
379+
schedule();
380+
}
381+
finish_wait(&netdev_unregistering_wq, &wait);
382+
}
383+
356384
/**
357385
* rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink.
358386
* @ops: struct rtnl_link_ops * to unregister
359387
*/
360388
void rtnl_link_unregister(struct rtnl_link_ops *ops)
361389
{
362-
rtnl_lock();
390+
/* Close the race with cleanup_net() */
391+
mutex_lock(&net_mutex);
392+
rtnl_lock_unregistering_all();
363393
__rtnl_link_unregister(ops);
364394
rtnl_unlock();
395+
mutex_unlock(&net_mutex);
365396
}
366397
EXPORT_SYMBOL_GPL(rtnl_link_unregister);
367398

0 commit comments

Comments
 (0)