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 | |
| 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>
| -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) { |
