diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index b7fee6e55f03..5cc719c81ea0 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1406,6 +1406,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, __must_hold(&tp_vars->common.unacked_lock) { struct batadv_tp_unacked *un, *new; + struct batadv_tp_unacked *safe; bool added = false; new = kmalloc_obj(*new, GFP_ATOMIC); @@ -1430,20 +1431,46 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, * seqno than all the others already stored. */ list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) { - /* check for duplicates */ - if (new->seqno == un->seqno) { - if (new->len > un->len) - un->len = new->len; - kfree(new); - added = true; - break; - } - - /* look for the right position */ + /* look for the right position - an un which is smaller */ if (batadv_seq_before(new->seqno, un->seqno)) continue; - /* as soon as an entry having a bigger seqno is found, the new + /* smaller/equal seqno was found but they might be directly + * after another or overlapping. keep only a single entry + * + * It is already known that: + * + * un->seqno <= new->seqno + * + * When establishing that: + * + * new->seqno <= un->seqno + un->len + * + * Then it is not necessary to add a new entry because the + * smaller/equal seqno of un might already contain the new + * received packet or we only add new data directly after + * the end of un. The latter can be identified using: + * + * un->seqno + un->len <= new->seqno + new->len + */ + if (!batadv_seq_before(un->seqno + un->len, new->seqno)) { + /* new data directly after un? */ + if (!batadv_seq_before(new->seqno + new->len, + un->seqno + un->len)) + un->len = new->seqno + new->len - un->seqno; + + /* un now represents both old un + new */ + kfree(new); + added = true; + + /* un has to be used to check if the gap to the next + * seqno range was closed + */ + new = un; + break; + } + + /* as soon as an entry having a smaller seqno is found, the new * one is attached _after_ it. In this way the list is kept in * ascending order */ @@ -1459,6 +1486,22 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, tp_vars->common.unacked_count++; } + /* check if new filled the gap to the next list entries */ + un = new; + list_for_each_entry_safe_continue(un, safe, &tp_vars->common.unacked_list, list) { + if (batadv_seq_before(new->seqno + new->len, un->seqno)) + break; + + /* next entry is overlapping or adjacent - combine both */ + if (batadv_seq_before(new->seqno + new->len, + un->seqno + un->len)) + new->len = un->seqno + un->len - new->seqno; + + list_del(&un->list); + kfree(un); + tp_vars->common.unacked_count--; + } + /* remove the last (biggest) unacked seqno when list is too large */ if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) { un = list_last_entry(&tp_vars->common.unacked_list, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index e1463a029e83..c2ab00d8ef16 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1332,7 +1332,7 @@ struct batadv_tp_unacked { u32 seqno; /** @len: length of the packet */ - u16 len; + u32 len; /** @list: list node for &batadv_tp_vars_common.unacked_list */ struct list_head list;