1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
250aadb33SDmitri Tikhonov#include <assert.h>
350aadb33SDmitri Tikhonov#include <inttypes.h>
450aadb33SDmitri Tikhonov#include <stdint.h>
550aadb33SDmitri Tikhonov#include <string.h>
6461e84d8SAmol Deshpande#ifdef WIN32
7461e84d8SAmol Deshpande#include <vc_compat.h>
8461e84d8SAmol Deshpande#endif
950aadb33SDmitri Tikhonov
1050aadb33SDmitri Tikhonov#include "lsquic_types.h"
1150aadb33SDmitri Tikhonov#include "lsquic_int_types.h"
1250aadb33SDmitri Tikhonov#include "lsquic_pacer.h"
1350aadb33SDmitri Tikhonov#include "lsquic_packet_common.h"
145392f7a3SLiteSpeed Tech#include "lsquic_packet_gquic.h"
1550aadb33SDmitri Tikhonov#include "lsquic_packet_out.h"
1650aadb33SDmitri Tikhonov#include "lsquic_util.h"
1750aadb33SDmitri Tikhonov
1850aadb33SDmitri Tikhonov#define LSQUIC_LOGGER_MODULE LSQLM_PACER
195392f7a3SLiteSpeed Tech#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(pacer->pa_conn)
2050aadb33SDmitri Tikhonov#include "lsquic_logger.h"
2150aadb33SDmitri Tikhonov
2250aadb33SDmitri Tikhonov#ifndef MAX
2350aadb33SDmitri Tikhonov#   define MAX(a, b) ((a) > (b) ? (a) : (b))
2450aadb33SDmitri Tikhonov#endif
2550aadb33SDmitri Tikhonov
2650aadb33SDmitri Tikhonov
2750aadb33SDmitri Tikhonovvoid
28a5fa05f9SDmitri Tikhonovlsquic_pacer_init (struct pacer *pacer, const struct lsquic_conn *conn,
295392f7a3SLiteSpeed Tech                                                unsigned clock_granularity)
3050aadb33SDmitri Tikhonov{
3150aadb33SDmitri Tikhonov    memset(pacer, 0, sizeof(*pacer));
3250aadb33SDmitri Tikhonov    pacer->pa_burst_tokens = 10;
335392f7a3SLiteSpeed Tech    pacer->pa_conn = conn;
346aba801dSDmitri Tikhonov    pacer->pa_clock_granularity = clock_granularity;
3550aadb33SDmitri Tikhonov}
3650aadb33SDmitri Tikhonov
3750aadb33SDmitri Tikhonov
3850aadb33SDmitri Tikhonovvoid
39a5fa05f9SDmitri Tikhonovlsquic_pacer_cleanup (struct pacer *pacer)
40bfc7bfd8SDmitri Tikhonov{
41bfc7bfd8SDmitri Tikhonov#ifndef NDEBUG
4290fe3b25SDmitri Tikhonov    LSQ_DEBUG("scheduled calls: %u", pacer->pa_stats.n_scheduled);
43bfc7bfd8SDmitri Tikhonov#endif
44bfc7bfd8SDmitri Tikhonov}
45bfc7bfd8SDmitri Tikhonov
46bfc7bfd8SDmitri Tikhonov
47bfc7bfd8SDmitri Tikhonovvoid
48a5fa05f9SDmitri Tikhonovlsquic_pacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
4950aadb33SDmitri Tikhonov                            int in_recovery, tx_time_f tx_time, void *tx_ctx)
5050aadb33SDmitri Tikhonov{
5150aadb33SDmitri Tikhonov    lsquic_time_t delay, sched_time;
5250aadb33SDmitri Tikhonov    int app_limited, making_up;
5350aadb33SDmitri Tikhonov
54bfc7bfd8SDmitri Tikhonov#ifndef NDEBUG
55bfc7bfd8SDmitri Tikhonov    ++pacer->pa_stats.n_scheduled;
56bfc7bfd8SDmitri Tikhonov#endif
575392f7a3SLiteSpeed Tech    ++pacer->pa_n_scheduled;
58bfc7bfd8SDmitri Tikhonov
5950aadb33SDmitri Tikhonov    if (n_in_flight == 0 && !in_recovery)
6050aadb33SDmitri Tikhonov    {
6150aadb33SDmitri Tikhonov        pacer->pa_burst_tokens = 10;
6250aadb33SDmitri Tikhonov        LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens);
6350aadb33SDmitri Tikhonov    }
6450aadb33SDmitri Tikhonov
6550aadb33SDmitri Tikhonov    if (pacer->pa_burst_tokens > 0)
6650aadb33SDmitri Tikhonov    {
6750aadb33SDmitri Tikhonov        --pacer->pa_burst_tokens;
6850aadb33SDmitri Tikhonov        pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
6950aadb33SDmitri Tikhonov        pacer->pa_next_sched = 0;
7050aadb33SDmitri Tikhonov        pacer->pa_last_delayed = 0;
7150aadb33SDmitri Tikhonov        LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
7250aadb33SDmitri Tikhonov        return;
7350aadb33SDmitri Tikhonov    }
7450aadb33SDmitri Tikhonov
7550aadb33SDmitri Tikhonov    sched_time = pacer->pa_now;
7650aadb33SDmitri Tikhonov    delay = tx_time(tx_ctx);
7750aadb33SDmitri Tikhonov    if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
7850aadb33SDmitri Tikhonov    {
7950aadb33SDmitri Tikhonov        pacer->pa_next_sched += delay;
8050aadb33SDmitri Tikhonov        app_limited = pacer->pa_last_delayed != 0
8150aadb33SDmitri Tikhonov            && pacer->pa_last_delayed + delay <= sched_time;
8250aadb33SDmitri Tikhonov        making_up = pacer->pa_next_sched <= sched_time;
8350aadb33SDmitri Tikhonov        LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited);
8450aadb33SDmitri Tikhonov        if (making_up && !app_limited)
8550aadb33SDmitri Tikhonov            pacer->pa_last_delayed = sched_time;
8650aadb33SDmitri Tikhonov        else
8750aadb33SDmitri Tikhonov        {
8850aadb33SDmitri Tikhonov            pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
8950aadb33SDmitri Tikhonov            pacer->pa_last_delayed = 0;
9050aadb33SDmitri Tikhonov        }
9150aadb33SDmitri Tikhonov    }
9250aadb33SDmitri Tikhonov    else
9350aadb33SDmitri Tikhonov        pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
9450aadb33SDmitri Tikhonov                                                    sched_time + delay);
9550aadb33SDmitri Tikhonov    LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
965392f7a3SLiteSpeed Tech                                pacer->pa_next_sched - pacer->pa_now);
9750aadb33SDmitri Tikhonov}
9850aadb33SDmitri Tikhonov
9950aadb33SDmitri Tikhonov
10050aadb33SDmitri Tikhonovvoid
101a5fa05f9SDmitri Tikhonovlsquic_pacer_loss_event (struct pacer *pacer)
10250aadb33SDmitri Tikhonov{
10350aadb33SDmitri Tikhonov    pacer->pa_burst_tokens = 0;
10450aadb33SDmitri Tikhonov    LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
10550aadb33SDmitri Tikhonov}
10650aadb33SDmitri Tikhonov
10750aadb33SDmitri Tikhonov
10850aadb33SDmitri Tikhonovint
109a5fa05f9SDmitri Tikhonovlsquic_pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
11050aadb33SDmitri Tikhonov{
11150aadb33SDmitri Tikhonov    int can;
11250aadb33SDmitri Tikhonov
11350aadb33SDmitri Tikhonov    if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
11450aadb33SDmitri Tikhonov        can = 1;
1156aba801dSDmitri Tikhonov    else if (pacer->pa_next_sched > pacer->pa_now + pacer->pa_clock_granularity)
11650aadb33SDmitri Tikhonov    {
11750aadb33SDmitri Tikhonov        pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
11850aadb33SDmitri Tikhonov        can = 0;
11950aadb33SDmitri Tikhonov    }
12050aadb33SDmitri Tikhonov    else
12150aadb33SDmitri Tikhonov        can = 1;
12250aadb33SDmitri Tikhonov
12350aadb33SDmitri Tikhonov    LSQ_DEBUG("%s: %d", __func__, can);
12450aadb33SDmitri Tikhonov    return can;
12550aadb33SDmitri Tikhonov}
12650aadb33SDmitri Tikhonov
12750aadb33SDmitri Tikhonov
128b8fa6195SDmitri Tikhonovint
129b8fa6195SDmitri Tikhonovlsquic_pacer_can_schedule_probe (const struct pacer *pacer,
130b8fa6195SDmitri Tikhonov                                    unsigned n_in_flight, lsquic_time_t tx_time)
131b8fa6195SDmitri Tikhonov{
132b8fa6195SDmitri Tikhonov    return pacer->pa_burst_tokens > 1 /* Double packet size, want two tokens */
133b8fa6195SDmitri Tikhonov        || n_in_flight == 0
134b8fa6195SDmitri Tikhonov        || pacer->pa_next_sched > pacer->pa_now + tx_time / 2;
135b8fa6195SDmitri Tikhonov}
136b8fa6195SDmitri Tikhonov
137b8fa6195SDmitri Tikhonov
13850aadb33SDmitri Tikhonovvoid
139a5fa05f9SDmitri Tikhonovlsquic_pacer_tick_in (struct pacer *pacer, lsquic_time_t now)
14050aadb33SDmitri Tikhonov{
141bdba46fdSDmitri Tikhonov    assert(now >= pacer->pa_now);
14250aadb33SDmitri Tikhonov    pacer->pa_now = now;
1435392f7a3SLiteSpeed Tech    if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
1445392f7a3SLiteSpeed Tech        pacer->pa_flags |= PA_DELAYED_ON_TICK_IN;
1455392f7a3SLiteSpeed Tech    pacer->pa_n_scheduled = 0;
1465392f7a3SLiteSpeed Tech}
1475392f7a3SLiteSpeed Tech
1485392f7a3SLiteSpeed Tech
1495392f7a3SLiteSpeed Techvoid
150a5fa05f9SDmitri Tikhonovlsquic_pacer_tick_out (struct pacer *pacer)
1515392f7a3SLiteSpeed Tech{
1525392f7a3SLiteSpeed Tech    if ((pacer->pa_flags & PA_DELAYED_ON_TICK_IN)
1535392f7a3SLiteSpeed Tech            && pacer->pa_n_scheduled == 0
1545392f7a3SLiteSpeed Tech                && pacer->pa_now > pacer->pa_next_sched)
1555392f7a3SLiteSpeed Tech    {
1565392f7a3SLiteSpeed Tech        LSQ_DEBUG("tick passed without scheduled packets: reset delayed flag");
1575392f7a3SLiteSpeed Tech        pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
1585392f7a3SLiteSpeed Tech    }
1595392f7a3SLiteSpeed Tech    pacer->pa_flags &= ~PA_DELAYED_ON_TICK_IN;
16050aadb33SDmitri Tikhonov}
161