Commit dca4a17d authored by Tuong Lien's avatar Tuong Lien Committed by David S. Miller

tipc: fix potential hanging after b/rcast changing

In commit c55c8eda ("tipc: smooth change between replicast and
broadcast"), we allow instant switching between replicast and broadcast
by sending a dummy 'SYN' packet on the last used link to synchronize
packets on the links. The 'SYN' message is an object of link congestion
also, so if that happens, a 'SOCK_WAKEUP' will be scheduled to be sent
back to the socket...
However, in that commit, we simply use the same socket 'cong_link_cnt'
counter for both the 'SYN' & normal payload message sending. Therefore,
if both the replicast & broadcast links are congested, the counter will
be not updated correctly but overwritten by the latter congestion.
Later on, when the 'SOCK_WAKEUP' messages are processed, the counter is
reduced one by one and eventually overflowed. Consequently, further
activities on the socket will only wait for the false congestion signal
to disappear but never been met.

Because sending the 'SYN' message is vital for the mechanism, it should
be done anyway. This commit fixes the issue by marking the message with
an error code e.g. 'TIPC_ERR_NO_PORT', so its sending should not face a
link congestion, there is no need to touch the socket 'cong_link_cnt'
either. In addition, in the event of any error (e.g. -ENOBUFS), we will
purge the entire payload message queue and make a return immediately.

Fixes: c55c8eda ("tipc: smooth change between replicast and broadcast")
Acked-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarTuong Lien <tuong.t.lien@dektech.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d5162f34
...@@ -305,17 +305,17 @@ static int tipc_rcast_xmit(struct net *net, struct sk_buff_head *pkts, ...@@ -305,17 +305,17 @@ static int tipc_rcast_xmit(struct net *net, struct sk_buff_head *pkts,
* @skb: socket buffer to copy * @skb: socket buffer to copy
* @method: send method to be used * @method: send method to be used
* @dests: destination nodes for message. * @dests: destination nodes for message.
* @cong_link_cnt: returns number of encountered congested destination links
* Returns 0 if success, otherwise errno * Returns 0 if success, otherwise errno
*/ */
static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb, static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb,
struct tipc_mc_method *method, struct tipc_mc_method *method,
struct tipc_nlist *dests, struct tipc_nlist *dests)
u16 *cong_link_cnt)
{ {
struct tipc_msg *hdr, *_hdr; struct tipc_msg *hdr, *_hdr;
struct sk_buff_head tmpq; struct sk_buff_head tmpq;
struct sk_buff *_skb; struct sk_buff *_skb;
u16 cong_link_cnt;
int rc = 0;
/* Is a cluster supporting with new capabilities ? */ /* Is a cluster supporting with new capabilities ? */
if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL)) if (!(tipc_net(net)->capabilities & TIPC_MCAST_RBCTL))
...@@ -343,18 +343,19 @@ static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb, ...@@ -343,18 +343,19 @@ static int tipc_mcast_send_sync(struct net *net, struct sk_buff *skb,
_hdr = buf_msg(_skb); _hdr = buf_msg(_skb);
msg_set_size(_hdr, MCAST_H_SIZE); msg_set_size(_hdr, MCAST_H_SIZE);
msg_set_is_rcast(_hdr, !msg_is_rcast(hdr)); msg_set_is_rcast(_hdr, !msg_is_rcast(hdr));
msg_set_errcode(_hdr, TIPC_ERR_NO_PORT);
__skb_queue_head_init(&tmpq); __skb_queue_head_init(&tmpq);
__skb_queue_tail(&tmpq, _skb); __skb_queue_tail(&tmpq, _skb);
if (method->rcast) if (method->rcast)
tipc_bcast_xmit(net, &tmpq, cong_link_cnt); rc = tipc_bcast_xmit(net, &tmpq, &cong_link_cnt);
else else
tipc_rcast_xmit(net, &tmpq, dests, cong_link_cnt); rc = tipc_rcast_xmit(net, &tmpq, dests, &cong_link_cnt);
/* This queue should normally be empty by now */ /* This queue should normally be empty by now */
__skb_queue_purge(&tmpq); __skb_queue_purge(&tmpq);
return 0; return rc;
} }
/* tipc_mcast_xmit - deliver message to indicated destination nodes /* tipc_mcast_xmit - deliver message to indicated destination nodes
...@@ -396,9 +397,14 @@ int tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts, ...@@ -396,9 +397,14 @@ int tipc_mcast_xmit(struct net *net, struct sk_buff_head *pkts,
msg_set_is_rcast(hdr, method->rcast); msg_set_is_rcast(hdr, method->rcast);
/* Switch method ? */ /* Switch method ? */
if (rcast != method->rcast) if (rcast != method->rcast) {
tipc_mcast_send_sync(net, skb, method, rc = tipc_mcast_send_sync(net, skb, method, dests);
dests, cong_link_cnt); if (unlikely(rc)) {
pr_err("Unable to send SYN: method %d, rc %d\n",
rcast, rc);
goto exit;
}
}
if (method->rcast) if (method->rcast)
rc = tipc_rcast_xmit(net, pkts, dests, cong_link_cnt); rc = tipc_rcast_xmit(net, pkts, dests, cong_link_cnt);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment