summaryrefslogtreecommitdiff
path: root/net/batman-adv/tp_meter.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/batman-adv/tp_meter.c')
-rw-r--r--net/batman-adv/tp_meter.c217
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