return path->cwnd - path->prep_in_flight;
}
+int quic_cwnd_may_increase(const struct quic_cc_path *path);
+
#endif /* USE_QUIC */
#endif /* _PROTO_QUIC_CC_H */
struct quic_cc_path *path = container_of(cc, struct quic_cc_path, cc);
return path->pacing_burst;
}
+
+/* Returns true if congestion window on path ought to be increased. */
+int quic_cwnd_may_increase(const struct quic_cc_path *path)
+{
+ /* RFC 9002 7.8. Underutilizing the Congestion Window
+ *
+ * When bytes in flight is smaller than the congestion window and
+ * sending is not pacing limited, the congestion window is
+ * underutilized. This can happen due to insufficient application data
+ * or flow control limits. When this occurs, the congestion window
+ * SHOULD NOT be increased in either slow start or congestion avoidance.
+ */
+
+ /* Consider that congestion window can be increased if it is at least
+ * half full or window size is less than 16k. These conditions should
+ * not be restricted too much to prevent slow window growing.
+ */
+ return 2 * path->in_flight >= path->cwnd || path->cwnd < 16384;
+}
inc = W_est_inc;
}
- path->cwnd += inc;
- path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
- path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ if (quic_cwnd_may_increase(path)) {
+ path->cwnd += inc;
+ path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
+ path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ }
leave:
TRACE_LEAVE(QUIC_EV_CONN_CC, cc->qc);
}
if (path->cwnd >= QUIC_CC_INFINITE_SSTHESH - acked)
goto out;
- path->cwnd += acked;
- path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ if (quic_cwnd_may_increase(path)) {
+ path->cwnd += acked;
+ path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ }
quic_cc_hystart_track_min_rtt(cc, h, path->loss.latest_rtt);
if (ev->ack.pn >= h->wnd_end)
h->wnd_end = UINT64_MAX;
}
}
else if (path->cwnd < QUIC_CC_INFINITE_SSTHESH - ev->ack.acked) {
- path->cwnd += ev->ack.acked;
- path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
+ if (quic_cwnd_may_increase(path)) {
+ path->cwnd += ev->ack.acked;
+ path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
+ }
}
/* Exit to congestion avoidance if slow start threshold is reached. */
if (path->cwnd >= c->ssthresh)
if (path->cwnd >= QUIC_CC_INFINITE_SSTHESH - acked)
goto out;
- path->cwnd += acked;
- path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ if (quic_cwnd_may_increase(path)) {
+ path->cwnd += acked;
+ path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ }
quic_cc_hystart_track_min_rtt(cc, h, path->loss.latest_rtt);
if (quic_cc_hystart_may_reenter_ss(h)) {
/* Exit to slow start */
path = container_of(cc, struct quic_cc_path, cc);
switch (ev->type) {
case QUIC_CC_EVT_ACK:
- path->cwnd += ev->ack.acked;
- path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
- path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ if (quic_cwnd_may_increase(path)) {
+ path->cwnd += ev->ack.acked;
+ path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
+ path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ }
/* Exit to congestion avoidance if slow start threshold is reached. */
if (path->cwnd > nr->ssthresh)
nr->state = QUIC_CC_ST_CA;
*/
acked = ev->ack.acked * path->mtu + nr->remain_acked;
nr->remain_acked = acked % path->cwnd;
- path->cwnd += acked / path->cwnd;
- path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
- path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ if (quic_cwnd_may_increase(path)) {
+ path->cwnd += acked / path->cwnd;
+ path->cwnd = QUIC_MIN(path->max_cwnd, path->cwnd);
+ path->mcwnd = QUIC_MAX(path->cwnd, path->mcwnd);
+ }
break;
}
list_for_each_entry_safe(pkt, tmp, newly_acked_pkts, list) {
pkt->pktns->tx.in_flight -= pkt->in_flight_len;
p->prep_in_flight -= pkt->in_flight_len;
- p->in_flight -= pkt->in_flight_len;
if (pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING)
p->ifae_pkts--;
/* If this packet contained an ACK frame, proceed to the
ev.ack.pn = pkt->pn_node.key;
/* Note that this event is not emitted for BBR. */
quic_cc_event(&p->cc, &ev);
+ p->in_flight -= pkt->in_flight_len;
if (drs && (pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING))
quic_cc_drs_update_rate_sample(drs, pkt, time_ns);
LIST_DEL_INIT(&pkt->list);