lsquic_sfcw.c revision 50aadb33
1/* Copyright (c) 2017 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <inttypes.h>
3#include <stdint.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sys/queue.h>
7
8#include "lsquic.h"
9#include "lsquic_int_types.h"
10#include "lsquic_conn_flow.h"
11#include "lsquic_types.h"
12#include "lsquic_rtt.h"
13#include "lsquic_sfcw.h"
14#include "lsquic_stream.h"
15#include "lsquic_conn_public.h"
16#include "lsquic_mm.h"
17#include "lsquic_engine_public.h"
18#include "lsquic_util.h"
19#include "lsquic_conn.h"
20#include "lsquic_ev_log.h"
21
22#define LSQUIC_LOGGER_MODULE LSQLM_SFCW
23#define LSQUIC_LOG_CONN_ID fc->sf_conn_pub->lconn->cn_cid
24#define LSQUIC_LOG_STREAM_ID fc->sf_stream_id
25#include "lsquic_logger.h"
26
27void
28lsquic_sfcw_init (struct lsquic_sfcw *fc, unsigned max_recv_window,
29                  struct lsquic_cfcw *cfcw, struct lsquic_conn_public *cpub,
30                  unsigned stream_id)
31{
32    memset(fc, 0, sizeof(*fc));
33    fc->sf_max_recv_win = max_recv_window;
34    fc->sf_cfcw = cfcw;
35    fc->sf_conn_pub = cpub;
36    fc->sf_stream_id = stream_id;
37    (void) lsquic_sfcw_fc_offsets_changed(fc);
38}
39
40
41static void
42sfcw_maybe_increase_max_window (struct lsquic_sfcw *fc)
43{
44    unsigned new_max_window, max_conn_window;
45
46    new_max_window = fc->sf_max_recv_win * 2;
47
48    /* Do not increase past explicitly specified maximum */
49    if (new_max_window > fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw)
50        new_max_window = fc->sf_conn_pub->enpub->enp_settings.es_max_sfcw;
51
52    /* Do not increase past the connection's maximum window size.  The
53     * connection's window will be increased separately, if possible.
54     *
55     * The reference implementation has the logic backwards:  Imagine
56     * several concurrent streams that are not being read from fast
57     * enough by the user code.  Each of them uses only a fraction
58     * of bandwidth.  Does it mean that the connection window must
59     * increase?  No.
60     */
61    max_conn_window = lsquic_cfcw_get_max_recv_window(fc->sf_cfcw);
62    if (new_max_window > max_conn_window)
63        new_max_window = max_conn_window;
64
65    if (new_max_window > fc->sf_max_recv_win)
66    {
67        LSQ_DEBUG("max window increase %u -> %u",
68            fc->sf_max_recv_win, new_max_window);
69        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID,
70            "max SFCW increase %u -> %u", fc->sf_max_recv_win,
71                                                            new_max_window);
72        fc->sf_max_recv_win = new_max_window;
73    }
74    else
75        LSQ_DEBUG("max window could use an increase, but we're stuck "
76            "at %u", fc->sf_max_recv_win);
77}
78
79
80int
81lsquic_sfcw_fc_offsets_changed (struct lsquic_sfcw *fc)
82{
83    lsquic_time_t since_last_update, srtt, now;
84
85    if (fc->sf_recv_off - fc->sf_read_off >= fc->sf_max_recv_win / 2)
86    {
87        LSQ_DEBUG("recv_off has not changed, still at %"PRIu64,
88                                                            fc->sf_recv_off);
89        return 0;
90    }
91
92    now = lsquic_time_now();
93    since_last_update = now - fc->sf_last_updated;
94    fc->sf_last_updated = now;
95
96    srtt = lsquic_rtt_stats_get_srtt(&fc->sf_conn_pub->rtt_stats);
97    if (since_last_update < srtt * 2)
98        sfcw_maybe_increase_max_window(fc);
99
100    fc->sf_recv_off = fc->sf_read_off + fc->sf_max_recv_win;
101    LSQ_DEBUG("recv_off changed: read_off: %"PRIu64"; "
102        "recv_off: %"PRIu64, fc->sf_read_off, fc->sf_recv_off);
103    return 1;
104}
105
106
107int
108lsquic_sfcw_set_max_recv_off (struct lsquic_sfcw *fc, uint64_t max_recv_off)
109{
110    if (max_recv_off <= fc->sf_recv_off)
111    {
112        if (!fc->sf_cfcw || lsquic_cfcw_incr_max_recv_off(fc->sf_cfcw,
113                                        max_recv_off - fc->sf_max_recv_off))
114        {
115            LSQ_DEBUG("max_recv_off goes from %"PRIu64" to %"PRIu64,
116                                            fc->sf_max_recv_off, max_recv_off);
117            fc->sf_max_recv_off = max_recv_off;
118            return 1;
119        }
120        else
121        {
122            /* cfcw prints its own warning */
123            return 0;
124        }
125    }
126    else
127    {
128        LSQ_WARN("flow control violation: received at offset %"PRIu64", "
129            "while flow control receive offset is %"PRIu64,
130            max_recv_off, fc->sf_recv_off);
131        return 0;
132    }
133}
134
135
136void
137lsquic_sfcw_set_read_off (struct lsquic_sfcw *fc, uint64_t off)
138{
139    if (fc->sf_cfcw)
140        lsquic_cfcw_incr_read_off(fc->sf_cfcw, off - fc->sf_read_off);
141    LSQ_DEBUG("read_off goes from %"PRIu64" to %"PRIu64,
142                                                fc->sf_read_off, off);
143    fc->sf_read_off = off;
144}
145