netdev, sched/wait: Fix sleeping inside wait event
rtnl_lock_unregistering*() take rtnl_lock() -- a mutex -- inside a wait loop. The wait loop relies on current->state to function, but so does mutex_lock(), nesting them makes for the inner to destroy the outer state. Fix this using the new wait_woken() bits. Reported-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: David S. Miller <davem@davemloft.net> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Cong Wang <cwang@twopensource.com> Cc: David Gibson <david@gibson.dropbear.id.au> Cc: Eric Biederman <ebiederm@xmission.com> Cc: Eric Dumazet <edumazet@google.com> Cc: Jamal Hadi Salim <jhs@mojatatu.com> Cc: Jerry Chu <hkchu@google.com> Cc: Jiri Pirko <jiri@resnulli.us> Cc: John Fastabend <john.fastabend@gmail.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Nicolas Dichtel <nicolas.dichtel@6wind.com> Cc: sfeldma@cumulusnetworks.com <sfeldma@cumulusnetworks.com> Cc: stephen hemminger <stephen@networkplumber.org> Cc: Tom Gundersen <teg@jklm.no> Cc: Tom Herbert <therbert@google.com> Cc: Veaceslav Falico <vfalico@gmail.com> Cc: Vlad Yasevich <vyasevic@redhat.com> Cc: netdev@vger.kernel.org Link: http://lkml.kernel.org/r/20141029173110.GE15602@worktop.programming.kicks-ass.net Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Родитель
eedf7e47da
Коммит
ff960a7317
|
@ -7196,11 +7196,10 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
|
||||||
*/
|
*/
|
||||||
struct net *net;
|
struct net *net;
|
||||||
bool unregistering;
|
bool unregistering;
|
||||||
DEFINE_WAIT(wait);
|
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
||||||
|
|
||||||
|
add_wait_queue(&netdev_unregistering_wq, &wait);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
prepare_to_wait(&netdev_unregistering_wq, &wait,
|
|
||||||
TASK_UNINTERRUPTIBLE);
|
|
||||||
unregistering = false;
|
unregistering = false;
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
list_for_each_entry(net, net_list, exit_list) {
|
list_for_each_entry(net, net_list, exit_list) {
|
||||||
|
@ -7212,9 +7211,10 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
|
||||||
if (!unregistering)
|
if (!unregistering)
|
||||||
break;
|
break;
|
||||||
__rtnl_unlock();
|
__rtnl_unlock();
|
||||||
schedule();
|
|
||||||
|
wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
|
||||||
}
|
}
|
||||||
finish_wait(&netdev_unregistering_wq, &wait);
|
remove_wait_queue(&netdev_unregistering_wq, &wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __net_exit default_device_exit_batch(struct list_head *net_list)
|
static void __net_exit default_device_exit_batch(struct list_head *net_list)
|
||||||
|
|
|
@ -365,11 +365,10 @@ static void rtnl_lock_unregistering_all(void)
|
||||||
{
|
{
|
||||||
struct net *net;
|
struct net *net;
|
||||||
bool unregistering;
|
bool unregistering;
|
||||||
DEFINE_WAIT(wait);
|
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
||||||
|
|
||||||
|
add_wait_queue(&netdev_unregistering_wq, &wait);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
prepare_to_wait(&netdev_unregistering_wq, &wait,
|
|
||||||
TASK_UNINTERRUPTIBLE);
|
|
||||||
unregistering = false;
|
unregistering = false;
|
||||||
rtnl_lock();
|
rtnl_lock();
|
||||||
for_each_net(net) {
|
for_each_net(net) {
|
||||||
|
@ -381,9 +380,10 @@ static void rtnl_lock_unregistering_all(void)
|
||||||
if (!unregistering)
|
if (!unregistering)
|
||||||
break;
|
break;
|
||||||
__rtnl_unlock();
|
__rtnl_unlock();
|
||||||
schedule();
|
|
||||||
|
wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
|
||||||
}
|
}
|
||||||
finish_wait(&netdev_unregistering_wq, &wait);
|
remove_wait_queue(&netdev_unregistering_wq, &wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче