diff options
author | chas williams - CONTRACTOR <chas@cmf.nrl.navy.mil> | 2014-08-12 08:12:26 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-08-13 23:04:46 -0400 |
commit | de713b57947a7a1b9cc605c0296ee14065cd86b6 (patch) | |
tree | d47fa60ae55956e23ff7c1d57c32bdcafd3f65f8 /net/atm | |
parent | 10545937e866ccdbb7ab583031dbdcc6b14e4eb4 (diff) |
atm/svc: Fix blocking in wait loop
One should not call blocking primitives inside a wait loop, since both
require task_struct::state to sleep, so the inner will destroy the
outer state.
sigd_enq() will possibly sleep for alloc_skb(). Move sigd_enq() before
prepare_to_wait() to avoid sleeping while waiting interruptibly. You do
not actually need to call sigd_enq() after the initial prepare_to_wait()
because we test the termination condition before calling schedule().
Based on suggestions from Peter Zijlstra.
Signed-off-by: Chas Williams <chas@cmf.n4rl.navy.mil>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/atm')
-rw-r--r-- | net/atm/svc.c | 60 |
1 files changed, 32 insertions, 28 deletions
diff --git a/net/atm/svc.c b/net/atm/svc.c index d8e5d0c2ebbc..1ba23f5018e7 100644 --- a/net/atm/svc.c +++ b/net/atm/svc.c | |||
@@ -50,12 +50,12 @@ static void svc_disconnect(struct atm_vcc *vcc) | |||
50 | 50 | ||
51 | pr_debug("%p\n", vcc); | 51 | pr_debug("%p\n", vcc); |
52 | if (test_bit(ATM_VF_REGIS, &vcc->flags)) { | 52 | if (test_bit(ATM_VF_REGIS, &vcc->flags)) { |
53 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | ||
54 | sigd_enq(vcc, as_close, NULL, NULL, NULL); | 53 | sigd_enq(vcc, as_close, NULL, NULL, NULL); |
55 | while (!test_bit(ATM_VF_RELEASED, &vcc->flags) && sigd) { | 54 | for (;;) { |
55 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | ||
56 | if (test_bit(ATM_VF_RELEASED, &vcc->flags) || !sigd) | ||
57 | break; | ||
56 | schedule(); | 58 | schedule(); |
57 | prepare_to_wait(sk_sleep(sk), &wait, | ||
58 | TASK_UNINTERRUPTIBLE); | ||
59 | } | 59 | } |
60 | finish_wait(sk_sleep(sk), &wait); | 60 | finish_wait(sk_sleep(sk), &wait); |
61 | } | 61 | } |
@@ -126,11 +126,12 @@ static int svc_bind(struct socket *sock, struct sockaddr *sockaddr, | |||
126 | } | 126 | } |
127 | vcc->local = *addr; | 127 | vcc->local = *addr; |
128 | set_bit(ATM_VF_WAITING, &vcc->flags); | 128 | set_bit(ATM_VF_WAITING, &vcc->flags); |
129 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | ||
130 | sigd_enq(vcc, as_bind, NULL, NULL, &vcc->local); | 129 | sigd_enq(vcc, as_bind, NULL, NULL, &vcc->local); |
131 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | 130 | for (;;) { |
132 | schedule(); | ||
133 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | 131 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); |
132 | if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) | ||
133 | break; | ||
134 | schedule(); | ||
134 | } | 135 | } |
135 | finish_wait(sk_sleep(sk), &wait); | 136 | finish_wait(sk_sleep(sk), &wait); |
136 | clear_bit(ATM_VF_REGIS, &vcc->flags); /* doesn't count */ | 137 | clear_bit(ATM_VF_REGIS, &vcc->flags); /* doesn't count */ |
@@ -202,15 +203,14 @@ static int svc_connect(struct socket *sock, struct sockaddr *sockaddr, | |||
202 | } | 203 | } |
203 | vcc->remote = *addr; | 204 | vcc->remote = *addr; |
204 | set_bit(ATM_VF_WAITING, &vcc->flags); | 205 | set_bit(ATM_VF_WAITING, &vcc->flags); |
205 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | ||
206 | sigd_enq(vcc, as_connect, NULL, NULL, &vcc->remote); | 206 | sigd_enq(vcc, as_connect, NULL, NULL, &vcc->remote); |
207 | if (flags & O_NONBLOCK) { | 207 | if (flags & O_NONBLOCK) { |
208 | finish_wait(sk_sleep(sk), &wait); | ||
209 | sock->state = SS_CONNECTING; | 208 | sock->state = SS_CONNECTING; |
210 | error = -EINPROGRESS; | 209 | error = -EINPROGRESS; |
211 | goto out; | 210 | goto out; |
212 | } | 211 | } |
213 | error = 0; | 212 | error = 0; |
213 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | ||
214 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | 214 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { |
215 | schedule(); | 215 | schedule(); |
216 | if (!signal_pending(current)) { | 216 | if (!signal_pending(current)) { |
@@ -297,11 +297,12 @@ static int svc_listen(struct socket *sock, int backlog) | |||
297 | goto out; | 297 | goto out; |
298 | } | 298 | } |
299 | set_bit(ATM_VF_WAITING, &vcc->flags); | 299 | set_bit(ATM_VF_WAITING, &vcc->flags); |
300 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | ||
301 | sigd_enq(vcc, as_listen, NULL, NULL, &vcc->local); | 300 | sigd_enq(vcc, as_listen, NULL, NULL, &vcc->local); |
302 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | 301 | for (;;) { |
303 | schedule(); | ||
304 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | 302 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); |
303 | if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) | ||
304 | break; | ||
305 | schedule(); | ||
305 | } | 306 | } |
306 | finish_wait(sk_sleep(sk), &wait); | 307 | finish_wait(sk_sleep(sk), &wait); |
307 | if (!sigd) { | 308 | if (!sigd) { |
@@ -387,15 +388,15 @@ static int svc_accept(struct socket *sock, struct socket *newsock, int flags) | |||
387 | } | 388 | } |
388 | /* wait should be short, so we ignore the non-blocking flag */ | 389 | /* wait should be short, so we ignore the non-blocking flag */ |
389 | set_bit(ATM_VF_WAITING, &new_vcc->flags); | 390 | set_bit(ATM_VF_WAITING, &new_vcc->flags); |
390 | prepare_to_wait(sk_sleep(sk_atm(new_vcc)), &wait, | ||
391 | TASK_UNINTERRUPTIBLE); | ||
392 | sigd_enq(new_vcc, as_accept, old_vcc, NULL, NULL); | 391 | sigd_enq(new_vcc, as_accept, old_vcc, NULL, NULL); |
393 | while (test_bit(ATM_VF_WAITING, &new_vcc->flags) && sigd) { | 392 | for (;;) { |
393 | prepare_to_wait(sk_sleep(sk_atm(new_vcc)), &wait, | ||
394 | TASK_UNINTERRUPTIBLE); | ||
395 | if (!test_bit(ATM_VF_WAITING, &new_vcc->flags) || !sigd) | ||
396 | break; | ||
394 | release_sock(sk); | 397 | release_sock(sk); |
395 | schedule(); | 398 | schedule(); |
396 | lock_sock(sk); | 399 | lock_sock(sk); |
397 | prepare_to_wait(sk_sleep(sk_atm(new_vcc)), &wait, | ||
398 | TASK_UNINTERRUPTIBLE); | ||
399 | } | 400 | } |
400 | finish_wait(sk_sleep(sk_atm(new_vcc)), &wait); | 401 | finish_wait(sk_sleep(sk_atm(new_vcc)), &wait); |
401 | if (!sigd) { | 402 | if (!sigd) { |
@@ -433,12 +434,14 @@ int svc_change_qos(struct atm_vcc *vcc, struct atm_qos *qos) | |||
433 | DEFINE_WAIT(wait); | 434 | DEFINE_WAIT(wait); |
434 | 435 | ||
435 | set_bit(ATM_VF_WAITING, &vcc->flags); | 436 | set_bit(ATM_VF_WAITING, &vcc->flags); |
436 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | ||
437 | sigd_enq2(vcc, as_modify, NULL, NULL, &vcc->local, qos, 0); | 437 | sigd_enq2(vcc, as_modify, NULL, NULL, &vcc->local, qos, 0); |
438 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && | 438 | for (;;) { |
439 | !test_bit(ATM_VF_RELEASED, &vcc->flags) && sigd) { | ||
440 | schedule(); | ||
441 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); | 439 | prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE); |
440 | if (!test_bit(ATM_VF_WAITING, &vcc->flags) || | ||
441 | test_bit(ATM_VF_RELEASED, &vcc->flags) || !sigd) { | ||
442 | break; | ||
443 | } | ||
444 | schedule(); | ||
442 | } | 445 | } |
443 | finish_wait(sk_sleep(sk), &wait); | 446 | finish_wait(sk_sleep(sk), &wait); |
444 | if (!sigd) | 447 | if (!sigd) |
@@ -529,18 +532,18 @@ static int svc_addparty(struct socket *sock, struct sockaddr *sockaddr, | |||
529 | 532 | ||
530 | lock_sock(sk); | 533 | lock_sock(sk); |
531 | set_bit(ATM_VF_WAITING, &vcc->flags); | 534 | set_bit(ATM_VF_WAITING, &vcc->flags); |
532 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | ||
533 | sigd_enq(vcc, as_addparty, NULL, NULL, | 535 | sigd_enq(vcc, as_addparty, NULL, NULL, |
534 | (struct sockaddr_atmsvc *) sockaddr); | 536 | (struct sockaddr_atmsvc *) sockaddr); |
535 | if (flags & O_NONBLOCK) { | 537 | if (flags & O_NONBLOCK) { |
536 | finish_wait(sk_sleep(sk), &wait); | ||
537 | error = -EINPROGRESS; | 538 | error = -EINPROGRESS; |
538 | goto out; | 539 | goto out; |
539 | } | 540 | } |
540 | pr_debug("added wait queue\n"); | 541 | pr_debug("added wait queue\n"); |
541 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | 542 | for (;;) { |
542 | schedule(); | ||
543 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | 543 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); |
544 | if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) | ||
545 | break; | ||
546 | schedule(); | ||
544 | } | 547 | } |
545 | finish_wait(sk_sleep(sk), &wait); | 548 | finish_wait(sk_sleep(sk), &wait); |
546 | error = xchg(&sk->sk_err_soft, 0); | 549 | error = xchg(&sk->sk_err_soft, 0); |
@@ -558,11 +561,12 @@ static int svc_dropparty(struct socket *sock, int ep_ref) | |||
558 | 561 | ||
559 | lock_sock(sk); | 562 | lock_sock(sk); |
560 | set_bit(ATM_VF_WAITING, &vcc->flags); | 563 | set_bit(ATM_VF_WAITING, &vcc->flags); |
561 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | ||
562 | sigd_enq2(vcc, as_dropparty, NULL, NULL, NULL, NULL, ep_ref); | 564 | sigd_enq2(vcc, as_dropparty, NULL, NULL, NULL, NULL, ep_ref); |
563 | while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) { | 565 | for (;;) { |
564 | schedule(); | ||
565 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); | 566 | prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); |
567 | if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd) | ||
568 | break; | ||
569 | schedule(); | ||
566 | } | 570 | } |
567 | finish_wait(sk_sleep(sk), &wait); | 571 | finish_wait(sk_sleep(sk), &wait); |
568 | if (!sigd) { | 572 | if (!sigd) { |