diff options
Diffstat (limited to 'net/batman-adv/tp_meter.c')
| -rw-r--r-- | net/batman-adv/tp_meter.c | 217 |
1 files changed, 133 insertions, 84 deletions
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index c2eea7dbc488..00467aa79de9 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst, } /** - * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists + * batadv_tp_sender_release() - release batadv_tp_sender * and queue for free after rcu grace period - * @ref: kref pointer of the batadv_tp_vars_common + * @ref: kref pointer of the batadv_tp_sender */ -static void batadv_tp_vars_common_release(struct kref *ref) +static void batadv_tp_sender_release(struct kref *ref) { - struct batadv_tp_vars_common *tp_vars; - struct batadv_tp_unacked *un, *safe; - - tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount); - - /* lock should not be needed because this object is now out of any - * context! - */ - spin_lock_bh(&tp_vars->unacked_lock); - list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { - list_del(&un->list); - kfree(un); - } - spin_unlock_bh(&tp_vars->unacked_lock); + struct batadv_tp_sender *tp_vars; - kfree_rcu(tp_vars, rcu); + tp_vars = container_of(ref, struct batadv_tp_sender, common.refcount); + kfree_rcu(tp_vars, common.rcu); } /** @@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars) if (!tp_vars) return; - kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release); + kref_put(&tp_vars->common.refcount, batadv_tp_sender_release); } /** @@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, init_waitqueue_head(&tp_vars->more_bytes); init_completion(&tp_vars->finished); - spin_lock_init(&tp_vars->common.unacked_lock); - INIT_LIST_HEAD(&tp_vars->common.unacked_list); - spin_lock_init(&tp_vars->cc_lock); tp_vars->prerandom_offset = 0; @@ -1252,6 +1237,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst } /** + * batadv_tp_receiver_release() - release batadv_tp_receiver + * and queue for free after rcu grace period + * @ref: kref pointer of the batadv_tp_receiver + */ +static void batadv_tp_receiver_release(struct kref *ref) +{ + struct batadv_tp_receiver *tp_vars; + struct batadv_tp_unacked *safe; + struct batadv_tp_unacked *un; + + tp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount); + + /* lock should not be needed because this object is now out of any + * context! + */ + spin_lock_bh(&tp_vars->ack_seqno_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { + list_del(&un->list); + kfree(un); + tp_vars->unacked_count--; + } + spin_unlock_bh(&tp_vars->ack_seqno_lock); + + kfree_rcu(tp_vars, common.rcu); +} + +/** * batadv_tp_receiver_put() - decrement the batadv_tp_receiver * refcounter and possibly release it * @tp_vars: the private data of the current TP meter session to be free'd @@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars) if (!tp_vars) return; - kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release); + kref_put(&tp_vars->common.refcount, batadv_tp_receiver_release); } /** @@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) if (batadv_tp_list_detach(&tp_vars->common)) batadv_tp_receiver_put(tp_vars); - spin_lock_bh(&tp_vars->common.unacked_lock); - list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) { + spin_lock_bh(&tp_vars->ack_seqno_lock); + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { list_del(&un->list); kfree(un); - tp_vars->common.unacked_count--; + tp_vars->unacked_count--; } - spin_unlock_bh(&tp_vars->common.unacked_lock); + spin_unlock_bh(&tp_vars->ack_seqno_lock); /* drop reference of timer */ if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1)) @@ -1403,69 +1415,106 @@ out: */ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, u32 seqno, u32 payload_len) - __must_hold(&tp_vars->common.unacked_lock) + __must_hold(&tp_vars->ack_seqno_lock) { - struct batadv_tp_unacked *un, *new; - bool added = false; - - new = kmalloc_obj(*new, GFP_ATOMIC); - if (unlikely(!new)) - return false; - - new->seqno = seqno; - new->len = payload_len; - - /* if the list is empty immediately attach this new object */ - if (list_empty(&tp_vars->common.unacked_list)) { - list_add(&new->list, &tp_vars->common.unacked_list); - tp_vars->common.unacked_count++; - return true; - } - - /* otherwise loop over the list and either drop the packet because this - * is a duplicate or store it at the right position. + struct list_head *pos = &tp_vars->unacked_list; + struct batadv_tp_unacked *new = NULL; + u32 end_seqno = seqno + payload_len; + struct batadv_tp_unacked *safe; + struct batadv_tp_unacked *un; + + /* loop over the list to find either an existing entry which the new + * seqno range can be merged with or the position at which a new entry + * has to be inserted. * * The iteration is done in the reverse way because it is likely that * the last received packet (the one being processed now) has a bigger * 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 */ - if (batadv_seq_before(new->seqno, un->seqno)) + list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) { + /* look for the right position - an un which is smaller */ + if (batadv_seq_before(seqno, un->seqno)) continue; - /* as soon as an entry having a bigger seqno is found, the new - * one is attached _after_ it. In this way the list is kept in - * ascending order + /* 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 <= seqno + * + * When establishing that: + * + * 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 <= end_seqno */ - list_add(&new->list, &un->list); - added = true; - tp_vars->common.unacked_count++; + if (!batadv_seq_before(un->seqno + un->len, seqno)) { + /* new data directly after un? */ + if (!batadv_seq_before(end_seqno, un->seqno + un->len)) + un->len = end_seqno - un->seqno; + + /* un now represents both old un + new range and has to + * be used to check if the gap to the next seqno range + * was closed + */ + new = un; + } else { + /* 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 + */ + pos = &un->list; + } + break; } - /* received packet with smallest seqno out of order; add it to front */ - if (!added) { - list_add(&new->list, &tp_vars->common.unacked_list); - tp_vars->common.unacked_count++; + /* no entry to merge with was found; insert a new one after the entry + * with the next smaller seqno (or at the front of the list when the + * new seqno is the smallest or the list is empty) + */ + if (!new) { + new = kmalloc_obj(*new, GFP_ATOMIC); + if (unlikely(!new)) + return false; + + new->seqno = seqno; + new->len = payload_len; + + list_add(&new->list, pos); + tp_vars->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->unacked_list, list) { + if (batadv_seq_before(end_seqno, un->seqno)) + break; + + /* next entry is overlapping or adjacent - combine both */ + if (batadv_seq_before(end_seqno, un->seqno + un->len)) { + end_seqno = un->seqno + un->len; + new->len = end_seqno - new->seqno; + } + + list_del(&un->list); + kfree(un); + tp_vars->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, + if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) { + un = list_last_entry(&tp_vars->unacked_list, struct batadv_tp_unacked, list); list_del(&un->list); kfree(un); - tp_vars->common.unacked_count--; + tp_vars->unacked_count--; } return true; @@ -1477,7 +1526,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars, * @tp_vars: the private data of the current TP meter session */ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) - __must_hold(&tp_vars->common.unacked_lock) + __must_hold(&tp_vars->ack_seqno_lock) { struct batadv_tp_unacked *un, *safe; u32 to_ack; @@ -1485,7 +1534,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) /* go through the unacked packet list and possibly ACK them as * well */ - list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) { + list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon * there is a gap between the last acked seqno and the seqno of * the packet under inspection @@ -1493,14 +1542,14 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars) if (batadv_seq_before(tp_vars->last_recv, un->seqno)) break; - to_ack = un->seqno + un->len - tp_vars->last_recv; + to_ack = un->seqno + un->len; - if (batadv_seq_before(tp_vars->last_recv, un->seqno + un->len)) - tp_vars->last_recv += to_ack; + if (batadv_seq_before(tp_vars->last_recv, to_ack)) + tp_vars->last_recv = to_ack; list_del(&un->list); kfree(un); - tp_vars->common.unacked_count--; + tp_vars->unacked_count--; } } @@ -1547,9 +1596,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, tp_vars->common.bat_priv = bat_priv; kref_init(&tp_vars->common.refcount); - spin_lock_init(&tp_vars->common.unacked_lock); - INIT_LIST_HEAD(&tp_vars->common.unacked_list); - tp_vars->common.unacked_count = 0; + spin_lock_init(&tp_vars->ack_seqno_lock); + INIT_LIST_HEAD(&tp_vars->unacked_list); + tp_vars->unacked_count = 0; kref_get(&tp_vars->common.refcount); timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0); @@ -1609,7 +1658,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, WRITE_ONCE(tp_vars->last_recv_time, jiffies); } - spin_lock_bh(&tp_vars->common.unacked_lock); + spin_lock_bh(&tp_vars->ack_seqno_lock); /* if the packet is a duplicate, it may be the case that an ACK has been * lost. Resend the ACK @@ -1625,7 +1674,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, * not been enqueued correctly */ if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) { - spin_unlock_bh(&tp_vars->common.unacked_lock); + spin_unlock_bh(&tp_vars->ack_seqno_lock); goto out; } @@ -1641,7 +1690,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, send_ack: to_ack = tp_vars->last_recv; - spin_unlock_bh(&tp_vars->common.unacked_lock); + spin_unlock_bh(&tp_vars->ack_seqno_lock); /* send the ACK. If the received packet was out of order, the ACK that * is going to be sent is a duplicate (the sender will count them and |
