diff mbox

net/atm: warning in alloc_tx/__might_sleep

Message ID CAM_iQpWaHX_orHbFjKcjBZL67b6LUAmOfvS1SQP13VPZDo-69w@mail.gmail.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Cong Wang Jan. 10, 2017, 5:35 p.m. UTC
On Mon, Jan 9, 2017 at 9:20 AM, Andrey Konovalov <andreyknvl@google.com> wrote:
> Hi!
>
> I've got the following error report while running the syzkaller fuzzer.
>
> On commit a121103c922847ba5010819a3f250f1f7fc84ab8 (4.10-rc3).
>
> A reproducer is attached.
>
> ------------[ cut here ]------------
> WARNING: CPU: 0 PID: 4114 at kernel/sched/core.c:7737 __might_sleep+0x149/0x1a0
> do not call blocking ops when !TASK_RUNNING; state=1 set at
> [<ffffffff813fcb22>] prepare_to_wait+0x182/0x530
> Modules linked in:
> CPU: 0 PID: 4114 Comm: a.out Not tainted 4.10.0-rc3+ #59
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> Call Trace:
>  __dump_stack lib/dump_stack.c:15
>  dump_stack+0x292/0x398 lib/dump_stack.c:51
>  __warn+0x19f/0x1e0 kernel/panic.c:547
>  warn_slowpath_fmt+0xc5/0x110 kernel/panic.c:562
>  __might_sleep+0x149/0x1a0 kernel/sched/core.c:7732
>  slab_pre_alloc_hook mm/slab.h:408
>  slab_alloc_node mm/slub.c:2634
>  kmem_cache_alloc_node+0x14a/0x280 mm/slub.c:2744
>  __alloc_skb+0x10f/0x800 net/core/skbuff.c:219
>  alloc_skb ./include/linux/skbuff.h:926
>  alloc_tx net/atm/common.c:75
>  vcc_sendmsg+0x5e8/0x1010 net/atm/common.c:609
>  sock_sendmsg_nosec net/socket.c:635
>  sock_sendmsg+0xca/0x110 net/socket.c:645
>  ___sys_sendmsg+0x9d2/0xae0 net/socket.c:1985
>  __sys_sendmsg+0x138/0x320 net/socket.c:2019
>  SYSC_sendmsg net/socket.c:2030
>  SyS_sendmsg+0x2d/0x50 net/socket.c:2026
>  entry_SYSCALL_64_fastpath+0x1f/0xc2 arch/x86/entry/entry_64.S:203
> RIP: 0033:0x7fcbacfddb79
> RSP: 002b:00007ffed8b5a7b8 EFLAGS: 00000206 ORIG_RAX: 000000000000002e
> RAX: ffffffffffffffda RBX: 00007ffed8b5a950 RCX: 00007fcbacfddb79
> RDX: 000000000000c000 RSI: 0000000020002000 RDI: 0000000000000003
> RBP: 0000000000400af0 R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000206 R12: 0000000000000000
> R13: 00007ffed8b5a950 R14: 0000000000000000 R15: 0000000000000000
> ---[ end trace 9edf2da84d8112da ]---
> atm:sigd_send: bad message type 0

The fix should be straight-forward. Mind to try the attached patch?

Comments

Eric Dumazet Jan. 10, 2017, 5:40 p.m. UTC | #1
On Tue, Jan 10, 2017 at 9:35 AM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> On Mon, Jan 9, 2017 at 9:20 AM, Andrey Konovalov <andreyknvl@google.com> wrote:
>
> The fix should be straight-forward. Mind to try the attached patch?


You forgot to remove schedule() ?

  schedule();
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
Cong Wang Jan. 10, 2017, 5:55 p.m. UTC | #2
On Tue, Jan 10, 2017 at 9:40 AM, Eric Dumazet <edumazet@google.com> wrote:
> On Tue, Jan 10, 2017 at 9:35 AM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
>> On Mon, Jan 9, 2017 at 9:20 AM, Andrey Konovalov <andreyknvl@google.com> wrote:
>>
>> The fix should be straight-forward. Mind to try the attached patch?
>
>
> You forgot to remove schedule() ?
>
>   schedule();
> + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);

Ah, of course, never even compile it... :-/
Francois Romieu Jan. 10, 2017, 10:47 p.m. UTC | #3
Eric Dumazet <edumazet@google.com> :
> On Tue, Jan 10, 2017 at 9:35 AM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> > On Mon, Jan 9, 2017 at 9:20 AM, Andrey Konovalov <andreyknvl@google.com> wrote:
> >
> > The fix should be straight-forward. Mind to try the attached patch?
> 
> 
> You forgot to remove schedule() ?

It may be clearer to split alloc_tx in two parts: only the unsleepable
"if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {" part of it
contributes to the inner "while (!(skb = alloc_tx(vcc, eff))) {" block.

See net/atm/common.c
[...]
static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size)
{
        struct sk_buff *skb;
        struct sock *sk = sk_atm(vcc);

        if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {
                pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n",
                         sk_wmem_alloc_get(sk), size, sk->sk_sndbuf);
                return NULL;
        }
        while (!(skb = alloc_skb(size, GFP_KERNEL)))
                schedule();
        pr_debug("%d += %d\n", sk_wmem_alloc_get(sk), skb->truesize);
        atomic_add(skb->truesize, &sk->sk_wmem_alloc);
        return skb;
}

The waiting stuff is related to vcc drain but the code makes it look as
if it were also related to skb alloc (it isn't).

It may be obvious for you but it took me a while to figure what the
code is supposed to achieve.
Andrey Konovalov Jan. 11, 2017, 10:18 a.m. UTC | #4
On Tue, Jan 10, 2017 at 6:40 PM, Eric Dumazet <edumazet@google.com> wrote:
> On Tue, Jan 10, 2017 at 9:35 AM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
>> On Mon, Jan 9, 2017 at 9:20 AM, Andrey Konovalov <andreyknvl@google.com> wrote:
>>
>> The fix should be straight-forward. Mind to try the attached patch?

Hi Cong,

Your patch with schedule() removed as suggested by Eric fixes the issue.

Tested-by: Andrey Konovalov <andreyknvl@google.com>

Thanks!

>
>
> You forgot to remove schedule() ?
>
>   schedule();
> + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
Eric Dumazet Jan. 11, 2017, 7:11 p.m. UTC | #5
On Tue, 2017-01-10 at 23:47 +0100, Francois Romieu wrote:
> Eric Dumazet <edumazet@google.com> :
> > On Tue, Jan 10, 2017 at 9:35 AM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> > > On Mon, Jan 9, 2017 at 9:20 AM, Andrey Konovalov <andreyknvl@google.com> wrote:
> > >
> > > The fix should be straight-forward. Mind to try the attached patch?
> > 
> > 
> > You forgot to remove schedule() ?
> 
> It may be clearer to split alloc_tx in two parts: only the unsleepable
> "if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {" part of it
> contributes to the inner "while (!(skb = alloc_tx(vcc, eff))) {" block.
> 
> See net/atm/common.c
> [...]
> static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size)
> {
>         struct sk_buff *skb;
>         struct sock *sk = sk_atm(vcc);
> 
>         if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) {
>                 pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n",
>                          sk_wmem_alloc_get(sk), size, sk->sk_sndbuf);
>                 return NULL;
>         }
>         while (!(skb = alloc_skb(size, GFP_KERNEL)))
>                 schedule();

Yeah, this code looks quite wrong anyway.

We can read it as an infinite loop in some stress conditions or memcg
constraints.


> The waiting stuff is related to vcc drain but the code makes it look as
> if it were also related to skb alloc (it isn't).
> 
> It may be obvious for you but it took me a while to figure what the
> code is supposed to achieve.
>
diff mbox

Patch

diff --git a/net/atm/common.c b/net/atm/common.c
index a3ca922..b7d9661 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -571,8 +571,8 @@  int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
 
 int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
 {
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 	struct sock *sk = sock->sk;
-	DEFINE_WAIT(wait);
 	struct atm_vcc *vcc;
 	struct sk_buff *skb;
 	int eff, error;
@@ -604,7 +604,7 @@  int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
 	}
 
 	eff = (size+3) & ~3; /* align to word boundary */
-	prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
+	add_wait_queue(sk_sleep(sk), &wait);
 	error = 0;
 	while (!(skb = alloc_tx(vcc, eff))) {
 		if (m->msg_flags & MSG_DONTWAIT) {
@@ -612,6 +612,7 @@  int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
 			break;
 		}
 		schedule();
+		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 		if (signal_pending(current)) {
 			error = -ERESTARTSYS;
 			break;
@@ -623,9 +624,8 @@  int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
 			send_sig(SIGPIPE, current, 0);
 			break;
 		}
-		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
 	}
-	finish_wait(sk_sleep(sk), &wait);
+	remove_wait_queue(sk_sleep(sk), &wait);
 	if (error)
 		goto out;
 	skb->dev = NULL; /* for paths shared with net_device interfaces */