diff options
author | Tung Nguyen <tung.q.nguyen@dektech.com.au> | 2019-02-24 22:57:20 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-02-26 17:50:50 -0500 |
commit | bfd07f3dd4f111b884d7922b37eb239280f83d8c (patch) | |
tree | 2d591b1836765b0f91ef21a7cd7b8ee0bd056e4b /net/tipc/socket.c | |
parent | bf48648d650db1146b75b9bd358502431e86cf4f (diff) |
tipc: fix race condition causing hung sendto
When sending multicast messages via blocking socket,
if sending link is congested (tsk->cong_link_cnt is set to 1),
the sending thread will be put into sleeping state. However,
tipc_sk_filter_rcv() is called under socket spin lock but
tipc_wait_for_cond() is not. So, there is no guarantee that
the setting of tsk->cong_link_cnt to 0 in tipc_sk_proto_rcv() in
CPU-1 will be perceived by CPU-0. If that is the case, the sending
thread in CPU-0 after being waken up, will continue to see
tsk->cong_link_cnt as 1 and put the sending thread into sleeping
state again. The sending thread will sleep forever.
CPU-0 | CPU-1
tipc_wait_for_cond() |
{ |
// condition_ = !tsk->cong_link_cnt |
while ((rc_ = !(condition_))) { |
... |
release_sock(sk_); |
wait_woken(); |
| if (!sock_owned_by_user(sk))
| tipc_sk_filter_rcv()
| {
| ...
| tipc_sk_proto_rcv()
| {
| ...
| tsk->cong_link_cnt--;
| ...
| sk->sk_write_space(sk);
| ...
| }
| ...
| }
sched_annotate_sleep(); |
lock_sock(sk_); |
remove_wait_queue(); |
} |
} |
This commit fixes it by adding memory barrier to tipc_sk_proto_rcv()
and tipc_wait_for_cond().
Acked-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: Tung Nguyen <tung.q.nguyen@dektech.com.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r-- | net/tipc/socket.c | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 684f2125fc6b..70343ac448b1 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
@@ -379,11 +379,13 @@ static int tipc_sk_sock_err(struct socket *sock, long *timeout) | |||
379 | 379 | ||
380 | #define tipc_wait_for_cond(sock_, timeo_, condition_) \ | 380 | #define tipc_wait_for_cond(sock_, timeo_, condition_) \ |
381 | ({ \ | 381 | ({ \ |
382 | DEFINE_WAIT_FUNC(wait_, woken_wake_function); \ | ||
382 | struct sock *sk_; \ | 383 | struct sock *sk_; \ |
383 | int rc_; \ | 384 | int rc_; \ |
384 | \ | 385 | \ |
385 | while ((rc_ = !(condition_))) { \ | 386 | while ((rc_ = !(condition_))) { \ |
386 | DEFINE_WAIT_FUNC(wait_, woken_wake_function); \ | 387 | /* coupled with smp_wmb() in tipc_sk_proto_rcv() */ \ |
388 | smp_rmb(); \ | ||
387 | sk_ = (sock_)->sk; \ | 389 | sk_ = (sock_)->sk; \ |
388 | rc_ = tipc_sk_sock_err((sock_), timeo_); \ | 390 | rc_ = tipc_sk_sock_err((sock_), timeo_); \ |
389 | if (rc_) \ | 391 | if (rc_) \ |
@@ -1983,6 +1985,8 @@ static void tipc_sk_proto_rcv(struct sock *sk, | |||
1983 | return; | 1985 | return; |
1984 | case SOCK_WAKEUP: | 1986 | case SOCK_WAKEUP: |
1985 | tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0); | 1987 | tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0); |
1988 | /* coupled with smp_rmb() in tipc_wait_for_cond() */ | ||
1989 | smp_wmb(); | ||
1986 | tsk->cong_link_cnt--; | 1990 | tsk->cong_link_cnt--; |
1987 | wakeup = true; | 1991 | wakeup = true; |
1988 | break; | 1992 | break; |