batman-adv: tp_meter: combine adjacent/overlapping unacked entries

Right at the point when the receiver gets the first packet with a seqno gap
(due to some packet loss/reordering), entries in the unacked list are
created. They are (besides direct seqno matches) are not combined. A lot
more then necessary entries are therefore created. Not for each gap but for
each packet.

This increases the memory consumption and management overhead. But it is
trivial to handle overlapping or adjacent sequence number ranges during the
insert. Only the handling of closed gaps by a new packets requires an extra
step after the insert.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Sven Eckelmann 2026-06-10 23:14:34 +02:00
parent 71b7987ec0
commit 936a1c10ce
No known key found for this signature in database
GPG Key ID: 4D0F772BD314F5CB
2 changed files with 55 additions and 12 deletions

View File

@ -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,

View File

@ -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;