lsquic_pacer.c revision 461e84d8
150aadb33SDmitri Tikhonov/* Copyright (c) 2017 LiteSpeed Technologies Inc.  See LICENSE. */
250aadb33SDmitri Tikhonov#include <assert.h>
350aadb33SDmitri Tikhonov#include <inttypes.h>
450aadb33SDmitri Tikhonov#include <stdint.h>
550aadb33SDmitri Tikhonov#ifndef NDEBUG
650aadb33SDmitri Tikhonov#include <stdlib.h>     /* getenv */
750aadb33SDmitri Tikhonov#endif
850aadb33SDmitri Tikhonov#include <string.h>
9461e84d8SAmol Deshpande#ifdef WIN32
10461e84d8SAmol Deshpande#include <vc_compat.h>
11461e84d8SAmol Deshpande#endif
1250aadb33SDmitri Tikhonov
1350aadb33SDmitri Tikhonov#include "lsquic_types.h"
1450aadb33SDmitri Tikhonov#include "lsquic_int_types.h"
1550aadb33SDmitri Tikhonov#include "lsquic_pacer.h"
1650aadb33SDmitri Tikhonov#include "lsquic_packet_common.h"
1750aadb33SDmitri Tikhonov#include "lsquic_packet_out.h"
1850aadb33SDmitri Tikhonov#include "lsquic_util.h"
1950aadb33SDmitri Tikhonov
2050aadb33SDmitri Tikhonov#define LSQUIC_LOGGER_MODULE LSQLM_PACER
2150aadb33SDmitri Tikhonov#define LSQUIC_LOG_CONN_ID pacer->pa_cid
2250aadb33SDmitri Tikhonov#include "lsquic_logger.h"
2350aadb33SDmitri Tikhonov
2450aadb33SDmitri Tikhonov#ifndef MAX
2550aadb33SDmitri Tikhonov#   define MAX(a, b) ((a) > (b) ? (a) : (b))
2650aadb33SDmitri Tikhonov#endif
2750aadb33SDmitri Tikhonov
2850aadb33SDmitri Tikhonov
2950aadb33SDmitri Tikhonovvoid
3050aadb33SDmitri Tikhonovpacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned max_intertick)
3150aadb33SDmitri Tikhonov{
3250aadb33SDmitri Tikhonov    memset(pacer, 0, sizeof(*pacer));
3350aadb33SDmitri Tikhonov    pacer->pa_burst_tokens = 10;
3450aadb33SDmitri Tikhonov    pacer->pa_cid = cid;
3550aadb33SDmitri Tikhonov    pacer->pa_max_intertick = max_intertick;
3650aadb33SDmitri Tikhonov#ifndef NDEBUG
3750aadb33SDmitri Tikhonov    const char *val;
3850aadb33SDmitri Tikhonov    if ((val = getenv("LSQUIC_PACER_INTERTICK")))
3950aadb33SDmitri Tikhonov    {
4050aadb33SDmitri Tikhonov        pacer->pa_flags |= PA_CONSTANT_INTERTICK;
4150aadb33SDmitri Tikhonov        pacer->pa_intertick_avg = atoi(val);
4250aadb33SDmitri Tikhonov    }
4350aadb33SDmitri Tikhonov#endif
4450aadb33SDmitri Tikhonov}
4550aadb33SDmitri Tikhonov
4650aadb33SDmitri Tikhonov
4750aadb33SDmitri Tikhonovvoid
48bfc7bfd8SDmitri Tikhonovpacer_cleanup (struct pacer *pacer)
49bfc7bfd8SDmitri Tikhonov{
50bfc7bfd8SDmitri Tikhonov#ifndef NDEBUG
51bfc7bfd8SDmitri Tikhonov    LSQ_NOTICE("scheduled calls: %u", pacer->pa_stats.n_scheduled);
52bfc7bfd8SDmitri Tikhonov#endif
53bfc7bfd8SDmitri Tikhonov}
54bfc7bfd8SDmitri Tikhonov
55bfc7bfd8SDmitri Tikhonov
56bfc7bfd8SDmitri Tikhonovvoid
5750aadb33SDmitri Tikhonovpacer_packet_scheduled (struct pacer *pacer, unsigned n_in_flight,
5850aadb33SDmitri Tikhonov                            int in_recovery, tx_time_f tx_time, void *tx_ctx)
5950aadb33SDmitri Tikhonov{
6050aadb33SDmitri Tikhonov    lsquic_time_t delay, sched_time;
6150aadb33SDmitri Tikhonov    int app_limited, making_up;
6250aadb33SDmitri Tikhonov
63bfc7bfd8SDmitri Tikhonov#ifndef NDEBUG
64bfc7bfd8SDmitri Tikhonov    ++pacer->pa_stats.n_scheduled;
65bfc7bfd8SDmitri Tikhonov#endif
66bfc7bfd8SDmitri Tikhonov
6750aadb33SDmitri Tikhonov    if (n_in_flight == 0 && !in_recovery)
6850aadb33SDmitri Tikhonov    {
6950aadb33SDmitri Tikhonov        pacer->pa_burst_tokens = 10;
7050aadb33SDmitri Tikhonov        LSQ_DEBUG("%s: replenish tokens: %u", __func__, pacer->pa_burst_tokens);
7150aadb33SDmitri Tikhonov    }
7250aadb33SDmitri Tikhonov
7350aadb33SDmitri Tikhonov    if (pacer->pa_burst_tokens > 0)
7450aadb33SDmitri Tikhonov    {
7550aadb33SDmitri Tikhonov        --pacer->pa_burst_tokens;
7650aadb33SDmitri Tikhonov        pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
7750aadb33SDmitri Tikhonov        pacer->pa_next_sched = 0;
7850aadb33SDmitri Tikhonov        pacer->pa_last_delayed = 0;
7950aadb33SDmitri Tikhonov        LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
8050aadb33SDmitri Tikhonov        return;
8150aadb33SDmitri Tikhonov    }
8250aadb33SDmitri Tikhonov
8350aadb33SDmitri Tikhonov    sched_time = pacer->pa_now;
8450aadb33SDmitri Tikhonov    delay = tx_time(tx_ctx);
8550aadb33SDmitri Tikhonov    if (pacer->pa_flags & PA_LAST_SCHED_DELAYED)
8650aadb33SDmitri Tikhonov    {
8750aadb33SDmitri Tikhonov        pacer->pa_next_sched += delay;
8850aadb33SDmitri Tikhonov        app_limited = pacer->pa_last_delayed != 0
8950aadb33SDmitri Tikhonov            && pacer->pa_last_delayed + delay <= sched_time;
9050aadb33SDmitri Tikhonov        making_up = pacer->pa_next_sched <= sched_time;
9150aadb33SDmitri Tikhonov        LSQ_DEBUG("making up: %d; app limited; %d", making_up, app_limited);
9250aadb33SDmitri Tikhonov        if (making_up && !app_limited)
9350aadb33SDmitri Tikhonov            pacer->pa_last_delayed = sched_time;
9450aadb33SDmitri Tikhonov        else
9550aadb33SDmitri Tikhonov        {
9650aadb33SDmitri Tikhonov            pacer->pa_flags &= ~PA_LAST_SCHED_DELAYED;
9750aadb33SDmitri Tikhonov            pacer->pa_last_delayed = 0;
9850aadb33SDmitri Tikhonov        }
9950aadb33SDmitri Tikhonov    }
10050aadb33SDmitri Tikhonov    else
10150aadb33SDmitri Tikhonov        pacer->pa_next_sched = MAX(pacer->pa_next_sched + delay,
10250aadb33SDmitri Tikhonov                                                    sched_time + delay);
10350aadb33SDmitri Tikhonov    LSQ_DEBUG("next_sched is set to %"PRIu64" usec from now",
10450aadb33SDmitri Tikhonov                                pacer->pa_next_sched - lsquic_time_now());
10550aadb33SDmitri Tikhonov}
10650aadb33SDmitri Tikhonov
10750aadb33SDmitri Tikhonov
10850aadb33SDmitri Tikhonovvoid
10950aadb33SDmitri Tikhonovpacer_loss_event (struct pacer *pacer)
11050aadb33SDmitri Tikhonov{
11150aadb33SDmitri Tikhonov    pacer->pa_burst_tokens = 0;
11250aadb33SDmitri Tikhonov    LSQ_DEBUG("%s: tokens: %u", __func__, pacer->pa_burst_tokens);
11350aadb33SDmitri Tikhonov}
11450aadb33SDmitri Tikhonov
11550aadb33SDmitri Tikhonov
11650aadb33SDmitri Tikhonovstatic unsigned
11750aadb33SDmitri Tikhonovclock_granularity (const struct pacer *pacer)
11850aadb33SDmitri Tikhonov{
119bfc7bfd8SDmitri Tikhonov    return pacer->pa_intertick_avg;
12050aadb33SDmitri Tikhonov}
12150aadb33SDmitri Tikhonov
12250aadb33SDmitri Tikhonov
12350aadb33SDmitri Tikhonovint
12450aadb33SDmitri Tikhonovpacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
12550aadb33SDmitri Tikhonov{
12650aadb33SDmitri Tikhonov    int can;
12750aadb33SDmitri Tikhonov
12850aadb33SDmitri Tikhonov    if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
12950aadb33SDmitri Tikhonov        can = 1;
13050aadb33SDmitri Tikhonov    else if (pacer->pa_next_sched > pacer->pa_now + clock_granularity(pacer))
13150aadb33SDmitri Tikhonov    {
13250aadb33SDmitri Tikhonov        pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
13350aadb33SDmitri Tikhonov        can = 0;
13450aadb33SDmitri Tikhonov    }
13550aadb33SDmitri Tikhonov    else
13650aadb33SDmitri Tikhonov        can = 1;
13750aadb33SDmitri Tikhonov
13850aadb33SDmitri Tikhonov    LSQ_DEBUG("%s: %d", __func__, can);
13950aadb33SDmitri Tikhonov    return can;
14050aadb33SDmitri Tikhonov}
14150aadb33SDmitri Tikhonov
14250aadb33SDmitri Tikhonov
14350aadb33SDmitri Tikhonov#define ALPHA_SHIFT 3
14450aadb33SDmitri Tikhonov#define BETA_SHIFT  2
14550aadb33SDmitri Tikhonov
14650aadb33SDmitri Tikhonovstatic void
14750aadb33SDmitri Tikhonovupdate_avg_intertick (struct pacer *pacer, unsigned intertick)
14850aadb33SDmitri Tikhonov{
14950aadb33SDmitri Tikhonov    unsigned diff;
15050aadb33SDmitri Tikhonov
15150aadb33SDmitri Tikhonov#ifndef NDEBUG
15250aadb33SDmitri Tikhonov    if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
15350aadb33SDmitri Tikhonov        return;
15450aadb33SDmitri Tikhonov#endif
15550aadb33SDmitri Tikhonov
15650aadb33SDmitri Tikhonov    if (pacer->pa_intertick_avg)
15750aadb33SDmitri Tikhonov    {
15850aadb33SDmitri Tikhonov        if (intertick > pacer->pa_intertick_avg)
15950aadb33SDmitri Tikhonov            diff = intertick - pacer->pa_intertick_avg;
16050aadb33SDmitri Tikhonov        else
16150aadb33SDmitri Tikhonov            diff = pacer->pa_intertick_avg - intertick;
16250aadb33SDmitri Tikhonov        pacer->pa_intertick_var -= pacer->pa_intertick_var >> BETA_SHIFT;
16350aadb33SDmitri Tikhonov        pacer->pa_intertick_var += diff >> BETA_SHIFT;
16450aadb33SDmitri Tikhonov        pacer->pa_intertick_avg -= pacer->pa_intertick_avg >> ALPHA_SHIFT;
16550aadb33SDmitri Tikhonov        pacer->pa_intertick_avg += intertick >> ALPHA_SHIFT;
16650aadb33SDmitri Tikhonov    }
16750aadb33SDmitri Tikhonov    else
16850aadb33SDmitri Tikhonov    {
16950aadb33SDmitri Tikhonov        pacer->pa_intertick_avg = intertick;
17050aadb33SDmitri Tikhonov        pacer->pa_intertick_var = intertick >> 1;
17150aadb33SDmitri Tikhonov    }
17250aadb33SDmitri Tikhonov}
17350aadb33SDmitri Tikhonov
17450aadb33SDmitri Tikhonov
17550aadb33SDmitri Tikhonovvoid
17650aadb33SDmitri Tikhonovpacer_tick (struct pacer *pacer, lsquic_time_t now)
17750aadb33SDmitri Tikhonov{
17850aadb33SDmitri Tikhonov    unsigned intertick;
17950aadb33SDmitri Tikhonov
18050aadb33SDmitri Tikhonov    assert(now > pacer->pa_now);
18150aadb33SDmitri Tikhonov    if (pacer->pa_now)
18250aadb33SDmitri Tikhonov    {
18350aadb33SDmitri Tikhonov        assert(now - pacer->pa_now < (1ULL << sizeof(unsigned) * 8));
18450aadb33SDmitri Tikhonov        intertick = now - pacer->pa_now;
18550aadb33SDmitri Tikhonov        LSQ_DEBUG("intertick estimate: %u; real value: %u; error: %d",
18650aadb33SDmitri Tikhonov            clock_granularity(pacer), intertick,
18750aadb33SDmitri Tikhonov            (int) clock_granularity(pacer) - (int) intertick);
18850aadb33SDmitri Tikhonov        update_avg_intertick(pacer, intertick);
18950aadb33SDmitri Tikhonov    }
19050aadb33SDmitri Tikhonov    pacer->pa_now = now;
19150aadb33SDmitri Tikhonov}
192