/* Measure RTT for each ack. */ static void tcp_illinois_acked(struct sock *sk, u32 pkts_acked, ktime_t last) { struct illinois *ca = inet_csk_ca(sk); u32 rtt; ca->acked = pkts_acked; if (ktime_equal(last, net_invalid_timestamp())) return; rtt = ktime_to_us(net_timedelta(last)); /* ignore bogus values, this prevents wraparound in alpha math */ if (rtt > RTT_MAX) rtt = RTT_MAX; /* keep track of minimum RTT seen so far */ if (ca->base_rtt > rtt) ca->base_rtt = rtt; /* and max */ if (ca->max_rtt < rtt) ca->max_rtt = rtt; ++ca->cnt_rtt; ca->sum_rtt += rtt; }
/** * tfrc_rx_hist_sample_rtt - Sample RTT from timestamp / CCVal * Based on ideas presented in RFC 4342, 8.1. Returns 0 if it was not able * to compute a sample with given data - calling function should check this. */ u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb) { u32 sample = 0, delta_v = SUB16(dccp_hdr(skb)->dccph_ccval, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); if (delta_v < 1 || delta_v > 4) { /* unsuitable CCVal delta */ if (h->rtt_sample_prev == 2) { /* previous candidate stored */ sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); if (sample) sample = 4 / sample * ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp); else /* * FIXME: This condition is in principle not * possible but occurs when CCID is used for * two-way data traffic. I have tried to trace * it, but the cause does not seem to be here. */ DCCP_BUG("please report to [email protected]" " => prev = %u, last = %u", tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); } else if (delta_v < 1) { h->rtt_sample_prev = 1; goto keep_ref_for_next_time; } } else if (delta_v == 4) /* optimal match */ sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp)); else { /* suboptimal match */ h->rtt_sample_prev = 2; goto keep_ref_for_next_time; } if (unlikely(sample > DCCP_SANE_RTT_MAX)) { DCCP_WARN("RTT sample %u too large, using max\n", sample); sample = DCCP_SANE_RTT_MAX; } h->rtt_sample_prev = 0; /* use current entry as next reference */ keep_ref_for_next_time: return sample; }
u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb) { u32 sample = 0, delta_v = SUB16(dccp_hdr(skb)->dccph_ccval, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); if (delta_v < 1 || delta_v > 4) { /* */ if (h->rtt_sample_prev == 2) { /* */ sample = SUB16(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); if (sample) sample = 4 / sample * ktime_us_delta(tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_tstamp, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp); else /* */ DCCP_BUG("please report to [email protected]" " => prev = %u, last = %u", tfrc_rx_hist_rtt_prev_s(h)->tfrchrx_ccval, tfrc_rx_hist_rtt_last_s(h)->tfrchrx_ccval); } else if (delta_v < 1) { h->rtt_sample_prev = 1; goto keep_ref_for_next_time; } } else if (delta_v == 4) /* */ sample = ktime_to_us(net_timedelta(tfrc_rx_hist_rtt_last_s(h)->tfrchrx_tstamp)); else { /* */ h->rtt_sample_prev = 2; goto keep_ref_for_next_time; } if (unlikely(sample > DCCP_SANE_RTT_MAX)) { DCCP_WARN("RTT sample %u too large, using max\n", sample); sample = DCCP_SANE_RTT_MAX; } h->rtt_sample_prev = 0; /* */ keep_ref_for_next_time: return sample; }
/* Do rtt sampling needed for Veno. */ static void tcp_veno_pkts_acked(struct sock *sk, u32 cnt, ktime_t last) { struct veno *veno = inet_csk_ca(sk); u32 vrtt; if (ktime_equal(last, net_invalid_timestamp())) return; /* Never allow zero rtt or baseRTT */ vrtt = ktime_to_us(net_timedelta(last)) + 1; /* Filter to find propagation delay: */ if (vrtt < veno->basertt) veno->basertt = vrtt; /* Find the min rtt during the last rtt to find * the current prop. delay + queuing delay: */ veno->minrtt = min(veno->minrtt, vrtt); veno->cntrtt++; }
/* Do RTT sampling needed for Vegas. * Basically we: * o min-filter RTT samples from within an RTT to get the current * propagation delay + queuing delay (we are min-filtering to try to * avoid the effects of delayed ACKs) * o min-filter RTT samples from a much longer window (forever for now) * to find the propagation delay (baseRTT) */ void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, ktime_t last) { cnt = cnt; struct vegas *vegas = inet_csk_ca(sk); u32 vrtt; if (ktime_equal(last, net_invalid_timestamp())) return; /* Never allow zero rtt or baseRTT */ vrtt = ktime_to_us(net_timedelta(last)) + 1; /* Filter to find propagation delay: */ if (vrtt < vegas->baseRTT) vegas->baseRTT = vrtt; /* Find the min RTT during the last RTT to find * the current prop. delay + queuing delay: */ vegas->minRTT = min(vegas->minRTT, vrtt); vegas->cntRTT++; }
/* Do RTT sampling needed for Vegas. * Basically we: * o min-filter RTT samples from within an RTT to get the current * propagation delay + queuing delay (we are min-filtering to try to * avoid the effects of delayed ACKs) * o min-filter RTT samples from a much longer window (forever for now) * to find the propagation delay (baseRTT) */ void tcp_sod_delay_pkts_acked(struct sock *sk, u32 cnt, ktime_t last) { struct tcp_sock *tp = tcp_sk(sk); struct sod_delay *sod = inet_csk_ca(sk); u64 vrtt; u64 qd_plus_td, remain_qd; u16 est_ql = 0; if (ktime_equal(last, net_invalid_timestamp())) return; /* Never allow zero rtt or baseRTT */ vrtt = ktime_to_us(net_timedelta(last)) + 1; /* Filter to find propagation delay: */ if (vrtt < sod->baseRTT) sod->baseRTT = vrtt; qd_plus_td = vrtt - sod->baseRTT; if (tp->td_last_index == 0) est_ql = 0; else { if (qd_plus_td < tp->ary_td[tp->td_last_index -1].td_i) est_ql = 1; else { u32 td_index = tp->td_last_index -1; remain_qd = qd_plus_td - tp->ary_td[td_index].td_i; while (remain_qd > 0 && td_index != tp->td_head_index) { if (tp->td_head_index >= tp->td_last_index && td_index == 0 ) { if (tp->td_count > 1) td_index = tp->td_count -1; else break; } else td_index--; if (remain_qd > tp->ary_td[td_index].td_i) { remain_qd -= tp->ary_td[td_index].td_i; est_ql++; } else { est_ql++; break; } } est_ql++; } } sod->curQL = est_ql; sod->minQL = min(sod->minQL, est_ql); //printf("current estimation: %lu\n", sod->curQL); /* Find the min RTT during the last RTT to find * the current prop. delay + queuing delay: */ sod->minRTT = min(sod->minRTT, vrtt); sod->cntRTT++; }
static void data_path_tx_func(unsigned long arg) { struct data_path *dp = (struct data_path *)arg; struct shm_rbctl *rbctl = dp->rbctl; struct shm_skctl *skctl = rbctl->skctl_va; struct shm_psd_skhdr *skhdr; struct sk_buff *packet; int slot = 0; int pending_slot; int free_slots; int prio; int remain_bytes; int used_bytes; int consumed_slot = 0; int consumed_packets = 0; int start_q_len; int max_tx_shots = dp->max_tx_shots; pending_slot = -1; remain_bytes = rbctl->tx_skbuf_size - sizeof(struct shm_psd_skhdr); used_bytes = 0; start_q_len = tx_q_length(dp); dp->stat.tx_sched_cnt++; while (consumed_slot < max_tx_shots) { if (!cp_is_synced) { tx_q_clean(dp); break; } free_slots = shm_free_tx_skbuf(rbctl); if (free_slots == 0) { /* * notify cp only if we still have packets in queue * otherwise, simply break * also check current fc status, if tx_stopped is * already sent to cp, do not try to interrupt cp again * it is useless, and just make cp busier * BTW: * this may have race condition here, but as cp side * have a watermark for resume interrupt, * we can assume it is safe */ if (tx_q_length(dp) && !rbctl->is_ap_xmit_stopped) { shm_notify_ap_tx_stopped(rbctl); acipc_notify_ap_psd_tx_stopped(); } break; } else if (free_slots == 1 && pending_slot != -1) { /* * the only left slot is our pending slot * check if we still have enough space in this * pending slot */ packet = tx_q_peek(dp, NULL); if (!packet) break; /* packet is too large, notify cp and break */ if (padded_size(packet->len) > remain_bytes && !rbctl->is_ap_xmit_stopped) { shm_notify_ap_tx_stopped(rbctl); acipc_notify_ap_psd_tx_stopped(); break; } } packet = tx_q_dequeue(dp, &prio); if (!packet) break; /* push to ring buffer */ /* we have one slot pending */ if (pending_slot != -1) { /* * the packet is too large for the pending slot * send out the pending slot firstly */ if (padded_size(packet->len) > remain_bytes) { shm_flush_dcache(rbctl, SHM_PACKET_PTR(rbctl->tx_va, pending_slot, rbctl->tx_skbuf_size), used_bytes + sizeof(struct shm_psd_skhdr)); skctl->ap_wptr = pending_slot; pending_slot = -1; consumed_slot++; dp->stat.tx_slots++; dp->stat.tx_free_bytes += remain_bytes; dp->stat.tx_used_bytes += used_bytes; } else slot = pending_slot; } /* * each priority has one hard limit to guarantee higher priority * packet is not affected by lower priority packet * if we reach this limit, we can only send higher priority * packets * but in the other hand, if this packet can be filled into our * pending slot, allow it anyway */ if (!has_enough_free_tx_slot(dp, free_slots, prio) && ((pending_slot == -1) || !dp->enable_piggyback)) { /* push back the packets and schedule delayed tx */ tx_q_queue_head(dp, packet, prio); __data_path_schedule_tx(dp, true); dp->stat.tx_force_sched_cnt++; break; } /* get a new slot from ring buffer */ if (pending_slot == -1) { slot = shm_get_next_tx_slot(dp->rbctl, skctl->ap_wptr); remain_bytes = rbctl->tx_skbuf_size - sizeof(struct shm_psd_skhdr); used_bytes = 0; pending_slot = slot; } consumed_packets++; dp->stat.tx_packets[prio]++; dp->stat.tx_bytes += packet->len; skhdr = (struct shm_psd_skhdr *) SHM_PACKET_PTR(rbctl->tx_va, slot, rbctl->tx_skbuf_size); /* we are sure our remains is enough for current packet */ skhdr->length = used_bytes + padded_size(packet->len); memcpy((unsigned char *)(skhdr + 1) + used_bytes, packet->data, packet->len); used_bytes += padded_size(packet->len); remain_bytes -= padded_size(packet->len); trace_psd_xmit(packet, slot); dp->stat.tx_packets_delay[prio] += ktime_to_ns(net_timedelta(skb_get_ktime(packet))); dev_kfree_skb_any(packet); } /* send out the pending slot */ if (pending_slot != -1) { shm_flush_dcache(rbctl, SHM_PACKET_PTR(rbctl->tx_va, pending_slot, rbctl->tx_skbuf_size), used_bytes + sizeof(struct shm_psd_skhdr)); skctl->ap_wptr = pending_slot; pending_slot = -1; consumed_slot++; dp->stat.tx_slots++; dp->stat.tx_free_bytes += remain_bytes; dp->stat.tx_used_bytes += used_bytes; } if (consumed_slot > 0) { trace_psd_xmit_irq(consumed_slot); acipc_notify_psd_packet_sent(); dp->stat.tx_interrupts++; dp->stat.tx_sched_q_len += start_q_len; } if (consumed_slot >= max_tx_shots) { data_path_schedule_tx(dp); dp->stat.tx_resched_cnt++; } /* * ring buffer is stopped, just notify upper layer * do not need to check is_tx_stopped here, as we need to handle * following situation: * a new on-demand PDP is activated after tx_stop is called */ if (rbctl->is_ap_xmit_stopped) { if (!dp->is_tx_stopped) pr_err("%s tx stop\n", __func__); dp->is_tx_stopped = true; /* notify upper layer tx stopped */ if (dp->cbs->tx_stop) dp->cbs->tx_stop(); /* reschedule tx to polling the ring buffer */ if (tx_q_length(dp)) __data_path_schedule_tx(dp, true); } /* * ring buffer is resumed and the remain packets * in queue is also sent out */ if (!rbctl->is_ap_xmit_stopped && dp->is_tx_stopped && tx_q_length(dp) == 0) { pr_err("%s tx resume\n", __func__); /* notify upper layer tx resumed */ if (dp->cbs->tx_resume) dp->cbs->tx_resume(); dp->is_tx_stopped = false; } }