/** *batadv_tp_update_rto()-calculatenewretransmissiontimeout *@tp_vars:theprivatedataofthecurrentTPmetersession *@new_rtt:newroundtriptimeinmsec
*/ staticvoid batadv_tp_update_rto(struct batadv_tp_vars *tp_vars,
u32 new_rtt)
{ long m = new_rtt;
/* RTT update *DetailsinSection2.2and2.3ofRFC6298 * *It'strickytounderstand.Don'tlosehairplease. *Inspiredbytcp_rtt_estimator()tcp_input.c
*/ if (tp_vars->srtt != 0) {
m -= (tp_vars->srtt >> 3); /* m is now error in rtt est */
tp_vars->srtt += m; /* rtt = 7/8 srtt + 1/8 new */ if (m < 0)
m = -m;
m -= (tp_vars->rttvar >> 2);
tp_vars->rttvar += m; /* mdev ~= 3/4 rttvar + 1/4 new */
} else { /* first measure getting in */
tp_vars->srtt = m << 3; /* take the measured time to be srtt */
tp_vars->rttvar = m << 1; /* new_rtt / 2 */
}
rcu_read_lock();
hlist_for_each_entry_rcu(pos, &bat_priv->tp_list, list) { if (!batadv_compare_eth(pos->other_end, dst)) continue;
/* most of the time this function is invoked during the normal *process..itmakessenstopaymorewhenthesessionis *finishedandtospeedtheprocessupduringthemeasurement
*/ if (unlikely(!kref_get_unless_zero(&pos->refcount))) continue;
rcu_read_lock();
hlist_for_each_entry_rcu(pos, &bat_priv->tp_list, list) { if (!batadv_compare_eth(pos->other_end, dst)) continue;
if (memcmp(pos->session, session, sizeof(pos->session)) != 0) continue;
/* most of the time this function is invoked during the normal *process..itmakessensetopaymorewhenthesessionis *finishedandtospeedtheprocessupduringthemeasurement
*/ if (unlikely(!kref_get_unless_zero(&pos->refcount))) continue;
/* 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);
/* drop list reference */
batadv_tp_vars_put(tp_vars);
atomic_dec(&tp_vars->bat_priv->tp_num);
/* kill the timer and remove its reference */
timer_delete_sync(&tp_vars->timer); /* the worker might have rearmed itself therefore we kill it again. Note *thatiftheworkershouldrunagainbeforeinvokingthefollowing *timer_delete(),itwouldnotre-armitselfonceagainbecausethestatus *isOFFnow
*/
timer_delete(&tp_vars->timer);
batadv_tp_vars_put(tp_vars);
}
/** *batadv_tp_reset_sender_timer()-reschedulethesendertimer *@tp_vars:theprivateTPmeterdataforthissession * *Reschedulethetimerusingtp_vars->rtoasdelay
*/ staticvoid batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)
{ /* most of the time this function is invoked while normal packet *reception...
*/ if (unlikely(atomic_read(&tp_vars->sending) == 0)) /* timer ref will be dropped in batadv_tp_sender_cleanup */ return;
/* if the user waited long enough...shutdown the test */ if (unlikely(tp_vars->rto >= BATADV_TP_MAX_RTO)) {
batadv_tp_sender_shutdown(tp_vars,
BATADV_TP_REASON_DST_UNREACHABLE); return;
}
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: RTO fired during test towards %pM! cwnd=%u new ss_thr=%u, resetting last_sent to %u\n",
tp_vars->other_end, tp_vars->cwnd, tp_vars->ss_threshold,
atomic_read(&tp_vars->last_acked));
tp_vars->cwnd = BATADV_TP_PLEN * 3;
spin_unlock_bh(&tp_vars->cwnd_lock);
/* resend the non-ACKed packets.. */
tp_vars->last_sent = atomic_read(&tp_vars->last_acked);
wake_up(&tp_vars->more_bytes);
/* find the tp_vars */
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session); if (unlikely(!tp_vars)) return;
if (unlikely(atomic_read(&tp_vars->sending) == 0)) goto out;
/* old ACK? silently drop it.. */ if (batadv_seq_before(ntohl(icmp->seqno),
(u32)atomic_read(&tp_vars->last_acked))) goto out;
primary_if = batadv_primary_if_get_selected(bat_priv); if (unlikely(!primary_if)) goto out;
orig_node = batadv_orig_hash_find(bat_priv, icmp->orig); if (unlikely(!orig_node)) goto out;
/* update RTO with the new sampled RTT, if any */
rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp); if (icmp->timestamp && rtt)
batadv_tp_update_rto(tp_vars, rtt);
/* ACK for new data... reset the timer */
batadv_tp_reset_sender_timer(tp_vars);
recv_ack = ntohl(icmp->seqno);
/* check if this ACK is a duplicate */ if (atomic_read(&tp_vars->last_acked) == recv_ack) {
atomic_inc(&tp_vars->dup_acks); if (atomic_read(&tp_vars->dup_acks) != 3) goto out;
if (recv_ack >= tp_vars->recover) goto out;
/* if this is the third duplicate ACK do Fast Retransmit */
batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
orig_node, recv_ack, packet_len,
icmp->session, icmp->uid,
jiffies_to_msecs(jiffies));
spin_lock_bh(&tp_vars->cwnd_lock);
/* Fast Recovery */
tp_vars->fast_recovery = true; /* Set recover to the last outstanding seqno when Fast Recovery *isentered.RFC6582,Section3.2,step1
*/
tp_vars->recover = tp_vars->last_sent;
tp_vars->ss_threshold = tp_vars->cwnd >> 1;
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: Fast Recovery, (cur cwnd=%u) ss_thr=%u last_sent=%u recv_ack=%u\n",
tp_vars->cwnd, tp_vars->ss_threshold,
tp_vars->last_sent, recv_ack);
tp_vars->cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 3 * mss,
mss);
tp_vars->dec_cwnd = 0;
tp_vars->last_sent = recv_ack;
spin_unlock_bh(&tp_vars->cwnd_lock);
} else { /* count the acked data */
atomic64_add(recv_ack - atomic_read(&tp_vars->last_acked),
&tp_vars->tot_sent); /* reset the duplicate ACKs counter */
atomic_set(&tp_vars->dup_acks, 0);
if (tp_vars->fast_recovery) { /* partial ACK */ if (batadv_seq_before(recv_ack, tp_vars->recover)) { /* this is another hole in the window. React *immediatelyasspecifiedbyNewReno(see *Section3.2ofRFC6582fordetails)
*/
dev_addr = primary_if->net_dev->dev_addr;
batadv_tp_send_msg(tp_vars, dev_addr,
orig_node, recv_ack,
packet_len, icmp->session,
icmp->uid,
jiffies_to_msecs(jiffies));
tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd,
mss, mss);
} else {
tp_vars->fast_recovery = false; /* set cwnd to the value of ss_threshold at the *momentthatFastRecoverywasentered. *RFC6582,Section3.2,step3
*/
cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 0,
mss);
tp_vars->cwnd = cwnd;
} goto move_twnd;
}
if (recv_ack - atomic_read(&tp_vars->last_acked) >= mss)
batadv_tp_update_cwnd(tp_vars, mss);
move_twnd: /* move the Transmit Window */
atomic_set(&tp_vars->last_acked, recv_ack);
}
/* assume that all the hard_interfaces have a correctly *configuredMTU,sousethemesh_ifaceMTUasMSS. *Thismightnotbetrueandinthatcasethefragmentation *shouldbeused. *Now,trytosendthepacketasitis
*/
payload_len = BATADV_TP_PLEN;
BUILD_BUG_ON(sizeof(struct batadv_icmp_tp_packet) > BATADV_TP_PLEN);
batadv_tp_reset_sender_timer(tp_vars);
/* queue the worker in charge of terminating the test */
queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work,
msecs_to_jiffies(tp_vars->test_length));
while (atomic_read(&tp_vars->sending) != 0) { if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) {
batadv_tp_wait_available(tp_vars, payload_len); continue;
}
/* to emulate normal unicast traffic, add to the payload len *thesizeoftheunicastheader
*/
packet_len = payload_len + sizeof(struct batadv_unicast_packet);
/* look for an already existing test towards this node */
spin_lock_bh(&bat_priv->tp_list_lock);
tp_vars = batadv_tp_list_find(bat_priv, dst); if (tp_vars) {
spin_unlock_bh(&bat_priv->tp_list_lock);
batadv_tp_vars_put(tp_vars);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: test to or from the same node already ongoing, aborting\n");
batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
dst, bat_priv, session_cookie); return;
}
if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) {
spin_unlock_bh(&bat_priv->tp_list_lock);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: too many ongoing sessions, aborting (SEND)\n");
batadv_tp_batctl_error_notify(BATADV_TP_REASON_TOO_MANY, dst,
bat_priv, session_cookie); return;
}
/* initialise the CWND to 3*MSS (Section 3.1 in RFC5681). *Forbatman-advtheMSSisthesizeofthepayloadreceivedbythe *mesh_interface,henceitsMTU
*/
tp_vars->cwnd = BATADV_TP_PLEN * 3; /* at the beginning initialise the SS threshold to the biggest possible *windowsize,hencetheAWNDsize
*/
tp_vars->ss_threshold = BATADV_TP_AWND;
/* RTO initial value is 3 seconds. *DetailsinSection2.1ofRFC6298
*/
tp_vars->rto = 1000;
tp_vars->srtt = 0;
tp_vars->rttvar = 0;
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: stopping test towards %pM\n", dst);
orig_node = batadv_orig_hash_find(bat_priv, dst); if (!orig_node) return;
tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig); if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: trying to interrupt an already over connection\n"); goto out;
}
/* if there is recent activity rearm the timer */ if (!batadv_has_timed_out(tp_vars->last_recv_time,
BATADV_TP_RECV_TIMEOUT)) { /* reset the receiver shutdown timer */
batadv_tp_reset_receiver_timer(tp_vars); return;
}
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Shutting down for inactivity (more than %dms) from %pM\n",
BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
/* send the ack */
r = batadv_send_skb_to_orig(skb, orig_node, NULL); if (unlikely(r < 0) || r == NET_XMIT_DROP) {
ret = BATADV_TP_REASON_DST_UNREACHABLE; goto out;
}
ret = 0;
spin_lock_bh(&tp_vars->unacked_lock); /* if the list is empty immediately attach this new object */ if (list_empty(&tp_vars->unacked_list)) {
list_add(&new->list, &tp_vars->unacked_list); goto out;
}
/* otherwise loop over the list and either drop the packet because this *isaduplicateorstoreitattherightposition. * *Theiterationisdoneinthereversewaybecauseitislikelythat *thelastreceivedpacket(theonebeingprocessednow)hasabigger *seqnothanalltheothersalreadystored.
*/
list_for_each_entry_reverse(un, &tp_vars->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)) continue;
/* as soon as an entry having a bigger seqno is found, the new *oneisattached_after_it.Inthiswaythelistiskeptin *ascendingorder
*/
list_add_tail(&new->list, &un->list);
added = true; break;
}
/* received packet with smallest seqno out of order; add it to front */ if (!added)
list_add(&new->list, &tp_vars->unacked_list);
/* go through the unacked packet list and possibly ACK them as *well
*/
spin_lock_bh(&tp_vars->unacked_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) { /* the list is ordered, therefore it is possible to stop as soon *thereisagapbetweenthelastackedseqnoandtheseqnoof *thepacketunderinspection
*/ if (batadv_seq_before(tp_vars->last_recv, un->seqno)) break;
seqno = ntohl(icmp->seqno); /* check if this is the first seqno. This means that if the *firstpacketislost,thetpmeterdoesnotworkanymore!
*/ if (seqno == BATADV_TP_FIRST_SEQ) {
tp_vars = batadv_tp_init_recv(bat_priv, icmp); if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: seqno != BATADV_TP_FIRST_SEQ cannot initiate connection\n"); goto out;
}
} else {
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session); if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Unexpected packet from %pM!\n",
icmp->orig); goto out;
}
}
if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: dropping packet: not expected (role=%u)\n",
tp_vars->role); goto out;
}
tp_vars->last_recv_time = jiffies;
/* if the packet is a duplicate, it may be the case that an ACK has been *lost.ResendtheACK
*/ if (batadv_seq_before(seqno, tp_vars->last_recv)) goto send_ack;
/* if the packet is out of order enqueue it */ if (ntohl(icmp->seqno) != tp_vars->last_recv) { /* exit immediately (and do not send any ACK) if the packet has *notbeenenqueuedcorrectly
*/ if (!batadv_tp_handle_out_of_order(tp_vars, skb)) goto out;
/* send a duplicate ACK */ goto send_ack;
}
/* if everything was fine count the ACKed bytes */
packet_size = skb->len - sizeof(struct batadv_unicast_packet);
tp_vars->last_recv += packet_size;
/* check if this ordered message filled a gap.... */
batadv_tp_ack_unordered(tp_vars);
send_ack: /* send the ACK. If the received packet was out of order, the ACK that *isgoingtobesentisaduplicate(thesenderwillcountthemand *possiblyenterFastRetransmitassoonasithasreached3)
*/
batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
icmp->timestamp, icmp->session, icmp->uid);
out:
batadv_tp_vars_put(tp_vars);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.