| From c704b1a8a72623d855422f94922e4c250556bf88 Mon Sep 17 00:00:00 2001 |
| From: Tuong Lien <tuong.t.lien@dektech.com.au> |
| Date: Tue, 10 Dec 2019 15:21:04 +0700 |
| Subject: [PATCH] tipc: fix retrans failure due to wrong destination |
| |
| commit abc9b4e0549b93fdaff56e9532bc49a2d7b04955 upstream. |
| |
| When a user message is sent, TIPC will check if the socket has faced a |
| congestion at link layer. If that happens, it will make a sleep to wait |
| for the congestion to disappear. This leaves a gap for other users to |
| take over the socket (e.g. multi threads) since the socket is released |
| as well. Also, in case of connectionless (e.g. SOCK_RDM), user is free |
| to send messages to various destinations (e.g. via 'sendto()'), then |
| the socket's preformatted header has to be updated correspondingly |
| prior to the actual payload message building. |
| |
| Unfortunately, the latter action is done before the first action which |
| causes a condition issue that the destination of a certain message can |
| be modified incorrectly in the middle, leading to wrong destination |
| when that message is built. Consequently, when the message is sent to |
| the link layer, it gets stuck there forever because the peer node will |
| simply reject it. After a number of retransmission attempts, the link |
| is eventually taken down and the retransmission failure is reported. |
| |
| This commit fixes the problem by rearranging the order of actions to |
| prevent the race condition from occurring, so the message building is |
| 'atomic' and its header will not be modified by anyone. |
| |
| Fixes: 365ad353c256 ("tipc: reduce risk of user starvation during link congestion") |
| Acked-by: Jon Maloy <jon.maloy@ericsson.com> |
| Signed-off-by: Tuong Lien <tuong.t.lien@dektech.com.au> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/net/tipc/socket.c b/net/tipc/socket.c |
| index 90ecca988d12..f5f027cf9a22 100644 |
| --- a/net/tipc/socket.c |
| +++ b/net/tipc/socket.c |
| @@ -1306,8 +1306,8 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) |
| struct tipc_msg *hdr = &tsk->phdr; |
| struct tipc_name_seq *seq; |
| struct sk_buff_head pkts; |
| - u32 dport, dnode = 0; |
| - u32 type, inst; |
| + u32 dport = 0, dnode = 0; |
| + u32 type = 0, inst = 0; |
| int mtu, rc; |
| |
| if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) |
| @@ -1360,23 +1360,11 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) |
| type = dest->addr.name.name.type; |
| inst = dest->addr.name.name.instance; |
| dnode = dest->addr.name.domain; |
| - msg_set_type(hdr, TIPC_NAMED_MSG); |
| - msg_set_hdr_sz(hdr, NAMED_H_SIZE); |
| - msg_set_nametype(hdr, type); |
| - msg_set_nameinst(hdr, inst); |
| - msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); |
| dport = tipc_nametbl_translate(net, type, inst, &dnode); |
| - msg_set_destnode(hdr, dnode); |
| - msg_set_destport(hdr, dport); |
| if (unlikely(!dport && !dnode)) |
| return -EHOSTUNREACH; |
| } else if (dest->addrtype == TIPC_ADDR_ID) { |
| dnode = dest->addr.id.node; |
| - msg_set_type(hdr, TIPC_DIRECT_MSG); |
| - msg_set_lookup_scope(hdr, 0); |
| - msg_set_destnode(hdr, dnode); |
| - msg_set_destport(hdr, dest->addr.id.ref); |
| - msg_set_hdr_sz(hdr, BASIC_H_SIZE); |
| } else { |
| return -EINVAL; |
| } |
| @@ -1387,6 +1375,22 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) |
| if (unlikely(rc)) |
| return rc; |
| |
| + if (dest->addrtype == TIPC_ADDR_NAME) { |
| + msg_set_type(hdr, TIPC_NAMED_MSG); |
| + msg_set_hdr_sz(hdr, NAMED_H_SIZE); |
| + msg_set_nametype(hdr, type); |
| + msg_set_nameinst(hdr, inst); |
| + msg_set_lookup_scope(hdr, tipc_node2scope(dnode)); |
| + msg_set_destnode(hdr, dnode); |
| + msg_set_destport(hdr, dport); |
| + } else { /* TIPC_ADDR_ID */ |
| + msg_set_type(hdr, TIPC_DIRECT_MSG); |
| + msg_set_lookup_scope(hdr, 0); |
| + msg_set_destnode(hdr, dnode); |
| + msg_set_destport(hdr, dest->addr.id.ref); |
| + msg_set_hdr_sz(hdr, BASIC_H_SIZE); |
| + } |
| + |
| skb_queue_head_init(&pkts); |
| mtu = tipc_node_get_mtu(net, dnode, tsk->portid); |
| rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts); |
| -- |
| 2.7.4 |
| |