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

tipc: reduce duplicate packets for unicast traffic

For unicast transmission, the current NACK sending althorithm is over-
active that forces the sending side to retransmit a packet that is not
really lost but just arrived at the receiving side with some delay, or
even retransmit same packets that have already been retransmitted
before. As a result, many duplicates are observed also under normal
condition, ie. without packet loss.

One example case is: node1 transmits 1 2 3 4 10 5 6 7 8 9, when node2
receives packet #10, it puts into the deferdq. When the packet #5 comes
it sends NACK with gap [6 - 9]. However, shortly after that, when
packet #6 arrives, it pulls out packet #10 from the deferfq, but it is
still out of order, so it makes another NACK with gap [7 - 9] and so on
... Finally, node1 has to retransmit the packets 5 6 7 8 9 a number of
times, but in fact all the packets are not lost at all, so duplicates!

This commit reduces duplicates by changing the condition to send NACK,
also restricting the retransmissions on individual packets via a timer
of about 1ms. However, it also needs to say that too tricky condition
for NACKs or too long timeout value for retransmissions will result in
performance reducing! The criterias in this commit are found to be
effective for both the requirements to reduce duplicates but not affect
performance.

The tipc_link_rcv() is also improved to only dequeue skb from the link
deferdq if it is expected (ie. its seqno <= rcv_nxt).
Acked-by: default avatarYing Xue <ying.xue@windriver.com>
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 9195948f
...@@ -209,6 +209,7 @@ enum { ...@@ -209,6 +209,7 @@ enum {
}; };
#define TIPC_BC_RETR_LIM msecs_to_jiffies(10) /* [ms] */ #define TIPC_BC_RETR_LIM msecs_to_jiffies(10) /* [ms] */
#define TIPC_UC_RETR_TIME (jiffies + msecs_to_jiffies(1))
/* /*
* Interval between NACKs when packets arrive out of order * Interval between NACKs when packets arrive out of order
...@@ -1305,6 +1306,10 @@ static void tipc_link_advance_transmq(struct tipc_link *l, u16 acked, u16 gap, ...@@ -1305,6 +1306,10 @@ static void tipc_link_advance_transmq(struct tipc_link *l, u16 acked, u16 gap,
kfree_skb(skb); kfree_skb(skb);
} else if (less_eq(seqno, acked + gap)) { } else if (less_eq(seqno, acked + gap)) {
/* retransmit skb */ /* retransmit skb */
if (time_before(jiffies, TIPC_SKB_CB(skb)->nxt_retr))
continue;
TIPC_SKB_CB(skb)->nxt_retr = TIPC_UC_RETR_TIME;
_skb = __pskb_copy(skb, MIN_H_SIZE, GFP_ATOMIC); _skb = __pskb_copy(skb, MIN_H_SIZE, GFP_ATOMIC);
if (!_skb) if (!_skb)
continue; continue;
...@@ -1380,6 +1385,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l, ...@@ -1380,6 +1385,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l,
struct sk_buff_head *xmitq) struct sk_buff_head *xmitq)
{ {
u32 def_cnt = ++l->stats.deferred_recv; u32 def_cnt = ++l->stats.deferred_recv;
u32 defq_len = skb_queue_len(&l->deferdq);
int match1, match2; int match1, match2;
if (link_is_bc_rcvlink(l)) { if (link_is_bc_rcvlink(l)) {
...@@ -1390,7 +1396,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l, ...@@ -1390,7 +1396,7 @@ static int tipc_link_build_nack_msg(struct tipc_link *l,
return 0; return 0;
} }
if ((skb_queue_len(&l->deferdq) == 1) || !(def_cnt % TIPC_NACK_INTV)) if (defq_len >= 3 && !((defq_len - 3) % 16))
tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, 0, xmitq); tipc_link_build_proto_msg(l, STATE_MSG, 0, 0, 0, 0, 0, xmitq);
return 0; return 0;
} }
...@@ -1404,29 +1410,29 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, ...@@ -1404,29 +1410,29 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
struct sk_buff_head *xmitq) struct sk_buff_head *xmitq)
{ {
struct sk_buff_head *defq = &l->deferdq; struct sk_buff_head *defq = &l->deferdq;
struct tipc_msg *hdr; struct tipc_msg *hdr = buf_msg(skb);
u16 seqno, rcv_nxt, win_lim; u16 seqno, rcv_nxt, win_lim;
int rc = 0; int rc = 0;
/* Verify and update link state */
if (unlikely(msg_user(hdr) == LINK_PROTOCOL))
return tipc_link_proto_rcv(l, skb, xmitq);
/* Don't send probe at next timeout expiration */
l->silent_intv_cnt = 0;
do { do {
hdr = buf_msg(skb); hdr = buf_msg(skb);
seqno = msg_seqno(hdr); seqno = msg_seqno(hdr);
rcv_nxt = l->rcv_nxt; rcv_nxt = l->rcv_nxt;
win_lim = rcv_nxt + TIPC_MAX_LINK_WIN; win_lim = rcv_nxt + TIPC_MAX_LINK_WIN;
/* Verify and update link state */
if (unlikely(msg_user(hdr) == LINK_PROTOCOL))
return tipc_link_proto_rcv(l, skb, xmitq);
if (unlikely(!link_is_up(l))) { if (unlikely(!link_is_up(l))) {
if (l->state == LINK_ESTABLISHING) if (l->state == LINK_ESTABLISHING)
rc = TIPC_LINK_UP_EVT; rc = TIPC_LINK_UP_EVT;
goto drop; goto drop;
} }
/* Don't send probe at next timeout expiration */
l->silent_intv_cnt = 0;
/* Drop if outside receive window */ /* Drop if outside receive window */
if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) { if (unlikely(less(seqno, rcv_nxt) || more(seqno, win_lim))) {
l->stats.duplicates++; l->stats.duplicates++;
...@@ -1457,7 +1463,7 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb, ...@@ -1457,7 +1463,7 @@ int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
rc |= tipc_link_build_state_msg(l, xmitq); rc |= tipc_link_build_state_msg(l, xmitq);
if (unlikely(rc & ~TIPC_LINK_SND_STATE)) if (unlikely(rc & ~TIPC_LINK_SND_STATE))
break; break;
} while ((skb = __skb_dequeue(defq))); } while ((skb = __tipc_skb_dequeue(defq, l->rcv_nxt)));
return rc; return rc;
drop: drop:
......
...@@ -1151,4 +1151,25 @@ static inline void tipc_skb_queue_splice_tail_init(struct sk_buff_head *list, ...@@ -1151,4 +1151,25 @@ static inline void tipc_skb_queue_splice_tail_init(struct sk_buff_head *list,
tipc_skb_queue_splice_tail(&tmp, head); tipc_skb_queue_splice_tail(&tmp, head);
} }
/* __tipc_skb_dequeue() - dequeue the head skb according to expected seqno
* @list: list to be dequeued from
* @seqno: seqno of the expected msg
*
* returns skb dequeued from the list if its seqno is less than or equal to
* the expected one, otherwise the skb is still hold
*
* Note: must be used with appropriate locks held only
*/
static inline struct sk_buff *__tipc_skb_dequeue(struct sk_buff_head *list,
u16 seqno)
{
struct sk_buff *skb = skb_peek(list);
if (skb && less_eq(buf_seqno(skb), seqno)) {
__skb_unlink(skb, list);
return skb;
}
return NULL;
}
#endif #endif
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