updated to upstream ran all tests again.

This commit is contained in:
Amol Deshpande 2018-03-12 19:56:06 -07:00
commit 5d77f141aa
25 changed files with 1048 additions and 895 deletions

View file

@ -1123,6 +1123,7 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
{
ssize_t enc_sz;
size_t bufsz;
unsigned sent_sz;
unsigned char *buf;
bufsz = lsquic_po_header_length(packet_out->po_flags) +
@ -1135,7 +1136,10 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
return ENCPA_NOMEM;
}
{
enc_sz = really_encrypt_packet(conn, packet_out, buf, bufsz);
sent_sz = enc_sz;
}
if (enc_sz < 0)
{
@ -1145,7 +1149,8 @@ encrypt_packet (lsquic_engine_t *engine, const lsquic_conn_t *conn,
packet_out->po_enc_data = buf;
packet_out->po_enc_data_sz = enc_sz;
packet_out->po_flags |= PO_ENCRYPTED;
packet_out->po_sent_sz = sent_sz;
packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ;
return ENCPA_OK;
}

View file

@ -94,6 +94,7 @@ enum full_conn_flags {
FC_FIRST_TICK = (1 <<19),
FC_TICK_CLOSE = (1 <<20), /* We returned TICK_CLOSE */
FC_HSK_FAILED = (1 <<21),
FC_HAVE_SAVED_ACK = (1 <<22),
};
#define FC_IMMEDIATE_CLOSE_FLAGS \
@ -190,6 +191,10 @@ struct full_conn
n_dup_packets,
n_err_packets;
unsigned long stream_data_sz;
unsigned long n_ticks;
unsigned n_acks_in,
n_acks_proc,
n_acks_merged[2];
} fc_stats;
#endif
#if KEEP_CLOSED_STREAM_HISTORY
@ -205,6 +210,8 @@ struct full_conn
#endif
STAILQ_HEAD(, stream_id_to_reset)
fc_stream_ids_to_reset;
struct short_ack_info fc_saved_ack_info;
lsquic_time_t fc_saved_ack_received;
};
@ -593,6 +600,7 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
conn->fc_conn.cn_esf->esf_destroy(conn->fc_conn.cn_enc_session);
lsquic_malo_destroy(conn->fc_pub.packet_out_malo);
#if FULL_CONN_STATS
LSQ_NOTICE("# ticks: %lu", conn->fc_stats.n_ticks);
LSQ_NOTICE("received %u packets, of which %u were not decryptable, %u were "
"dups and %u were errors; sent %u packets, avg stream data per outgoing"
" packet is %lu bytes",
@ -600,6 +608,9 @@ full_conn_ci_destroy (lsquic_conn_t *lconn)
conn->fc_stats.n_dup_packets, conn->fc_stats.n_err_packets,
conn->fc_stats.n_packets_out,
conn->fc_stats.stream_data_sz / conn->fc_stats.n_packets_out);
LSQ_NOTICE("ACKs: in: %u; processed: %u; merged to: new %u, old %u",
conn->fc_stats.n_acks_in, conn->fc_stats.n_acks_proc,
conn->fc_stats.n_acks_merged[0], conn->fc_stats.n_acks_merged[1]);
#endif
while ((sitr = STAILQ_FIRST(&conn->fc_stream_ids_to_reset)))
{
@ -1095,34 +1106,214 @@ log_invalid_ack_frame (struct full_conn *conn, const unsigned char *p,
}
static int
process_ack (struct full_conn *conn, struct ack_info *acki,
lsquic_time_t received)
{
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_proc;
#endif
LSQ_DEBUG("Processing ACK");
if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl, acki, received))
{
if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl))
lsquic_rechist_stop_wait(&conn->fc_rechist,
lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl) + 1);
return 0;
}
else
{
ABORT_ERROR("Received invalid ACK");
return -1;
}
}
static int
process_saved_ack (struct full_conn *conn, int restore_parsed_ack)
{
struct ack_info *const acki = conn->fc_pub.mm->acki;
struct lsquic_packno_range range = { 0 };
unsigned n_ranges = 0, n_timestamps = 0;
lsquic_time_t lack_delta = 0;
int retval;
if (restore_parsed_ack)
{
n_ranges = acki->n_ranges;
n_timestamps = acki->n_timestamps;
lack_delta = acki->lack_delta;
range = acki->ranges[0];
}
acki->n_ranges = 1;
acki->n_timestamps = conn->fc_saved_ack_info.sai_n_timestamps;
acki->lack_delta = conn->fc_saved_ack_info.sai_lack_delta;
acki->ranges[0] = conn->fc_saved_ack_info.sai_range;
retval = process_ack(conn, acki, conn->fc_saved_ack_received);
if (restore_parsed_ack)
{
acki->n_ranges = n_ranges;
acki->n_timestamps = n_timestamps;
acki->lack_delta = lack_delta;
acki->ranges[0] = range;
}
return retval;
}
static int
new_ack_is_superset (const struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
new_range = &new->ranges[ new->n_ranges - 1 ];
return new_range->low <= old->sai_range.low
&& new_range->high >= old->sai_range.high;
}
static int
merge_saved_to_new (const struct short_ack_info *old, struct ack_info *new)
{
struct lsquic_packno_range *smallest_range;
assert(new->n_ranges > 1);
smallest_range = &new->ranges[ new->n_ranges - 1 ];
if (old->sai_range.high <= smallest_range->high
&& old->sai_range.high >= smallest_range->low
&& old->sai_range.low < smallest_range->low)
{
smallest_range->low = old->sai_range.low;
return 1;
}
else
return 0;
}
static int
merge_new_to_saved (struct short_ack_info *old, const struct ack_info *new)
{
const struct lsquic_packno_range *new_range;
assert(new->n_ranges == 1);
new_range = &new->ranges[0];
/* Only merge if new is higher, for simplicity. This is also the
* expected case.
*/
if (new_range->high > old->sai_range.high
&& new_range->low > old->sai_range.low)
{
old->sai_range.high = new_range->high;
return 1;
}
else
return 0;
}
static unsigned
process_ack_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
const unsigned char *p, size_t len)
{
const int parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len,
conn->fc_pub.mm->acki);
struct ack_info *const new_acki = conn->fc_pub.mm->acki;
int parsed_len;
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_in;
#endif
parsed_len = conn->fc_conn.cn_pf->pf_parse_ack_frame(p, len, new_acki);
if (parsed_len < 0)
return 0;
if (packet_in->pi_packno > conn->fc_max_ack_packno)
goto err;
if (packet_in->pi_packno <= conn->fc_max_ack_packno)
{
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, conn->fc_pub.mm->acki);
if (0 == lsquic_send_ctl_got_ack(&conn->fc_send_ctl,
conn->fc_pub.mm->acki, packet_in->pi_received))
{
conn->fc_max_ack_packno = packet_in->pi_packno;
if (lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl))
lsquic_rechist_stop_wait(&conn->fc_rechist,
lsquic_send_ctl_largest_ack2ed(&conn->fc_send_ctl) + 1);
}
else
{
log_invalid_ack_frame(conn, p, parsed_len, conn->fc_pub.mm->acki);
ABORT_ERROR("Received invalid ACK");
LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
return parsed_len;
}
EV_LOG_ACK_FRAME_IN(LSQUIC_LOG_CONN_ID, new_acki);
conn->fc_max_ack_packno = packet_in->pi_packno;
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
{
LSQ_DEBUG("old ack [%"PRIu64"-%"PRIu64"]",
conn->fc_saved_ack_info.sai_range.high,
conn->fc_saved_ack_info.sai_range.low);
const int is_superset = new_ack_is_superset(&conn->fc_saved_ack_info,
new_acki);
const int is_1range = new_acki->n_ranges == 1;
switch (
(is_superset << 1)
| (is_1range << 0))
/* | |
| |
V V */ {
case (0 << 1) | (0 << 0):
if (merge_saved_to_new(&conn->fc_saved_ack_info, new_acki))
{
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_merged[0]
#endif
;
}
else
process_saved_ack(conn, 1);
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received))
goto err;
break;
case (0 << 1) | (1 << 0):
if (merge_new_to_saved(&conn->fc_saved_ack_info, new_acki))
{
#if FULL_CONN_STATS
++conn->fc_stats.n_acks_merged[1]
#endif
;
}
else
{
process_saved_ack(conn, 1);
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
}
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_received = packet_in->pi_received;
break;
case (1 << 1) | (0 << 0):
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
if (0 != process_ack(conn, new_acki, packet_in->pi_received))
goto err;
break;
case (1 << 1) | (1 << 0):
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->fc_saved_ack_received = packet_in->pi_received;
break;
}
}
else
LSQ_DEBUG("Ignore old ack (max %"PRIu64")", conn->fc_max_ack_packno);
else if (new_acki->n_ranges == 1)
{
conn->fc_saved_ack_info.sai_n_timestamps = new_acki->n_timestamps;
conn->fc_saved_ack_info.sai_lack_delta = new_acki->lack_delta;
conn->fc_saved_ack_info.sai_range = new_acki->ranges[0];
conn->fc_saved_ack_received = packet_in->pi_received;
conn->fc_flags |= FC_HAVE_SAVED_ACK;
}
else if (0 != process_ack(conn, new_acki, packet_in->pi_received))
goto err;
return parsed_len;
err:
log_invalid_ack_frame(conn, p, parsed_len, new_acki);
return 0;
}
@ -2151,7 +2342,7 @@ generate_ack_frame (struct full_conn *conn)
(gaf_rechist_first_f) lsquic_rechist_first,
(gaf_rechist_next_f) lsquic_rechist_next,
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
&conn->fc_rechist, now, &has_missing);
&conn->fc_rechist, now, &has_missing, &packet_out->po_ack2ed);
if (w < 0) {
ABORT_ERROR("generating ACK frame failed: %d", errno);
return;
@ -2306,6 +2497,10 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
} \
} while (0)
#if FULL_CONN_STATS
++conn->fc_stats.n_ticks;
#endif
if (LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)
&& conn->fc_mem_logged_last + 1000000 <= now)
{
@ -2315,6 +2510,13 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
assert(!(conn->fc_conn.cn_flags & LSCONN_RW_PENDING));
if (conn->fc_flags & FC_HAVE_SAVED_ACK)
{
(void) /* If there is an error, we'll fail shortly */
process_saved_ack(conn, 0);
conn->fc_flags &= ~FC_HAVE_SAVED_ACK;
}
lsquic_send_ctl_tick(&conn->fc_send_ctl, now);
lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 1);
CLOSE_IF_NECESSARY();

View file

@ -83,10 +83,12 @@ typedef struct lsquic_packet_out
* further writes are allowed.
*/
PO_SCHED = (1 <<14), /* On scheduled queue */
PO_SENT_SZ = (1 <<15),
} po_flags:16;
enum quic_ft_bit po_frame_types:16; /* Bitmask of QUIC_FRAME_* */
unsigned short po_data_sz; /* Number of usable bytes in data */
unsigned short po_enc_data_sz; /* Number of usable bytes in data */
unsigned short po_sent_sz; /* If PO_SENT_SZ is set, real size of sent buffer. */
unsigned short po_regen_sz; /* Number of bytes at the beginning
* of data containing bytes that are
* not to be retransmitted, e.g. ACK
@ -94,6 +96,9 @@ typedef struct lsquic_packet_out
*/
unsigned short po_n_alloc; /* Total number of bytes allocated in po_data */
unsigned char *po_data;
lsquic_packno_t po_ack2ed; /* If packet has ACK frame, value of
* largest acked in it.
*/
/* A lot of packets contain data belonging to only one stream. Thus,
* `one' is used first. If this is not enough, any number of
@ -142,6 +147,23 @@ typedef struct lsquic_packet_out
((p)->po_data_sz + lsquic_po_header_length((p)->po_flags) \
+ QUIC_PACKET_HASH_SZ)
#if __GNUC__
#if LSQUIC_EXTRA_CHECKS
#define lsquic_packet_out_sent_sz(p) ( \
__builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ? \
(assert((p)->po_sent_sz == lsquic_packet_out_total_sz(p)), \
(p)->po_sent_sz) : lsquic_packet_out_total_sz(p))
# else
#define lsquic_packet_out_sent_sz(p) ( \
__builtin_expect(((p)->po_flags & PO_SENT_SZ), 1) ? \
(p)->po_sent_sz : lsquic_packet_out_total_sz(p))
#endif
#else
# define lsquic_packet_out_sent_sz(p) ( \
(p)->po_flags & PO_SENT_SZ ? \
(p)->po_sent_sz : lsquic_packet_out_total_sz(p))
#endif
#define lsquic_packet_out_verneg(p) \
(((p)->po_flags & (PO_NOENCRYPT|PO_VERNEG)) == (PO_NOENCRYPT|PO_VERNEG))

View file

@ -31,6 +31,13 @@ typedef struct ack_info
#endif
} ack_info_t;
struct short_ack_info
{
unsigned sai_n_timestamps;
lsquic_time_t sai_lack_delta;
struct lsquic_packno_range sai_range;
};
#define largest_acked(acki) (+(acki)->ranges[0].high)
#define smallest_acked(acki) (+(acki)->ranges[(acki)->n_ranges - 1].low)
@ -86,13 +93,11 @@ struct parse_funcs
int
(*pf_parse_ack_frame) (const unsigned char *buf, size_t buf_len,
ack_info_t *ack_info);
lsquic_packno_t
(*pf_parse_ack_high) (const unsigned char *buf, size_t buf_len);
int
(*pf_gen_ack_frame) (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f, gaf_rechist_next_f,
gaf_rechist_largest_recv_f, void *rechist, lsquic_time_t now,
int *has_missing);
int *has_missing, lsquic_packno_t *largest_received);
int
(*pf_gen_stop_waiting_frame) (unsigned char *buf, size_t buf_len,
lsquic_packno_t cur_packno, enum lsquic_packno_bits,

View file

@ -267,28 +267,6 @@ gquic_ietf_gen_stream_frame (unsigned char *buf, size_t buf_len, uint32_t stream
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_ietf_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
unsigned n_blocks_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1248((type >> 2) & 3);
n_blocks_len = !!(type & 0x10);
assert(parse_frame_type_gquic_Q041(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + n_blocks_len + 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1 + n_blocks_len + 1, largest_obs_len);
return packno;
}
int
gquic_ietf_parse_ack_frame (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
@ -383,7 +361,8 @@ int
gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
@ -539,6 +518,7 @@ gquic_ietf_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
p += ack_block_len;
}
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
@ -555,7 +535,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q041 =
.pf_parse_stream_frame_header_sz = gquic_ietf_parse_stream_frame_header_sz,
.pf_parse_stream_frame = gquic_ietf_parse_stream_frame,
.pf_parse_ack_frame = gquic_ietf_parse_ack_frame,
.pf_parse_ack_high = gquic_ietf_parse_ack_high,
.pf_gen_ack_frame = gquic_ietf_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,

View file

@ -367,26 +367,6 @@ gquic_be_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
lsquic_packno_t
gquic_be_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
lsquic_packno_t packno;
type = buf[0];
largest_obs_len = twobit_to_1246((type >> 2) & 3);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
READ_UINT(packno, 64, buf + 1, largest_obs_len);
return packno;
}
static int
parse_ack_frame_without_blocks (const unsigned char *buf, size_t buf_len,
ack_info_t *ack)
@ -813,7 +793,8 @@ int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received)
{
lsquic_packno_t tmp_packno;
const struct lsquic_packno_range *const first = rechist_first(rechist);
@ -966,6 +947,7 @@ gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
*p = 0;
++p;
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
@ -982,7 +964,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q039 =
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_be_parse_stream_frame,
.pf_parse_ack_frame = gquic_be_parse_ack_frame,
.pf_parse_ack_high = gquic_be_parse_ack_high,
.pf_gen_ack_frame = gquic_be_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame,

View file

@ -139,6 +139,6 @@ int
gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing);
void *rechist, lsquic_time_t now, int *has_missing, lsquic_packno_t *);
#endif

View file

@ -337,24 +337,6 @@ gquic_le_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
}
/* This is a special function: it is used to extract the largest observed
* packet number from ACK frame that we ourselves generated. This allows
* us to skip some checks.
*/
static lsquic_packno_t
gquic_le_parse_ack_high (const unsigned char *buf, size_t buf_len)
{
unsigned char type;
unsigned largest_obs_len;
type = buf[0];
largest_obs_len = flag_to_pkt_num_len(type >> 2);
assert(parse_frame_type_gquic_Q035_thru_Q039(type) == QUIC_FRAME_ACK);
assert(buf_len >= 1 + largest_obs_len);
return get_vary_len_int64(buf + 1, largest_obs_len);
}
/* Return parsed (used) buffer length.
* If parsing failed, negative value is returned.
*/
@ -705,7 +687,8 @@ static int
gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing)
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received)
{
const struct lsquic_packno_range *const first = rechist_first(rechist);
if (!first)
@ -841,6 +824,7 @@ gquic_le_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
*p = 0;
++p;
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
@ -857,7 +841,6 @@ const struct parse_funcs lsquic_parse_funcs_gquic_le =
.pf_parse_stream_frame_header_sz = parse_stream_frame_header_sz_gquic,
.pf_parse_stream_frame = gquic_le_parse_stream_frame,
.pf_parse_ack_frame = gquic_le_parse_ack_frame,
.pf_parse_ack_high = gquic_le_parse_ack_high,
.pf_gen_ack_frame = gquic_le_gen_ack_frame,
.pf_gen_stop_waiting_frame = gquic_le_gen_stop_waiting_frame,
.pf_parse_stop_waiting_frame = gquic_le_parse_stop_waiting_frame,

View file

@ -410,12 +410,9 @@ send_ctl_unacked_append (struct lsquic_send_ctl *ctl,
static void
send_ctl_unacked_remove (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
struct lsquic_packet_out *packet_out, unsigned packet_sz)
{
unsigned packet_sz;
TAILQ_REMOVE(&ctl->sc_unacked_packets, packet_out, po_next);
packet_sz = lsquic_packet_out_total_sz(packet_out);
assert(ctl->sc_bytes_unacked_all >= packet_sz);
ctl->sc_bytes_unacked_all -= packet_sz;
ctl->sc_n_in_flight_all -= 1;
@ -479,47 +476,56 @@ lsquic_send_ctl_sent_packet (lsquic_send_ctl_t *ctl,
sizeof(frames), packet_out->po_frame_types));
if (account)
ctl->sc_bytes_out -= lsquic_packet_out_total_sz(packet_out);
if (0 == lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno))
lsquic_senhist_add(&ctl->sc_senhist, packet_out->po_packno);
send_ctl_unacked_append(ctl, packet_out);
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
send_ctl_unacked_append(ctl, packet_out);
if (packet_out->po_frame_types & QFRAME_RETRANSMITTABLE_MASK)
{
if (!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
set_retx_alarm(ctl);
if (ctl->sc_n_in_flight_retx == 1)
ctl->sc_flags |= SC_WAS_QUIET;
}
/* TODO: Do we really want to use those for RTT info? Revisit this. */
/* Hold on to packets that are not retransmittable because we need them
* to sample RTT information. They are released when ACK is received.
*/
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_total_sent;
#endif
return 0;
if (!lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX))
set_retx_alarm(ctl);
if (ctl->sc_n_in_flight_retx == 1)
ctl->sc_flags |= SC_WAS_QUIET;
}
else
return -1;
/* TODO: Do we really want to use those for RTT info? Revisit this. */
/* Hold on to packets that are not retransmittable because we need them
* to sample RTT information. They are released when ACK is received.
*/
#if LSQUIC_SEND_STATS
++ctl->sc_stats.n_total_sent;
#endif
lsquic_send_ctl_sanity_check(ctl);
return 0;
}
static void
take_rtt_sample (lsquic_send_ctl_t *ctl, const lsquic_packet_out_t *packet_out,
take_rtt_sample (lsquic_send_ctl_t *ctl,
lsquic_time_t now, lsquic_time_t lack_delta)
{
assert(packet_out->po_sent);
lsquic_time_t measured_rtt = now - packet_out->po_sent;
if (packet_out->po_packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
const lsquic_packno_t packno = ctl->sc_largest_acked_packno;
const lsquic_time_t sent = ctl->sc_largest_acked_sent_time;
const lsquic_time_t measured_rtt = now - sent;
if (packno > ctl->sc_max_rtt_packno && lack_delta < measured_rtt)
{
ctl->sc_max_rtt_packno = packet_out->po_packno;
ctl->sc_max_rtt_packno = packno;
lsquic_rtt_stats_update(&ctl->sc_conn_pub->rtt_stats, measured_rtt, lack_delta);
LSQ_DEBUG("packno %"PRIu64"; rtt: %"PRIu64"; delta: %"PRIu64"; "
"new srtt: %"PRIu64, packet_out->po_packno, measured_rtt, lack_delta,
"new srtt: %"PRIu64, packno, measured_rtt, lack_delta,
lsquic_rtt_stats_get_srtt(&ctl->sc_conn_pub->rtt_stats));
}
}
static void
send_ctl_release_enc_data (struct lsquic_send_ctl *ctl,
struct lsquic_packet_out *packet_out)
{
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
packet_out->po_flags &= ~PO_ENCRYPTED;
packet_out->po_enc_data = NULL;
}
/* Returns true if packet was rescheduled, false otherwise. In the latter
* case, you should not dereference packet_out after the function returns.
*/
@ -527,14 +533,13 @@ static int
send_ctl_handle_lost_packet (lsquic_send_ctl_t *ctl,
lsquic_packet_out_t *packet_out)
{
unsigned packet_sz;
assert(ctl->sc_n_in_flight_all);
send_ctl_unacked_remove(ctl, packet_out);
if (packet_out->po_flags & PO_ENCRYPTED) {
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
packet_out->po_flags &= ~PO_ENCRYPTED;
packet_out->po_enc_data = NULL;
}
packet_sz = lsquic_packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_release_enc_data(ctl, packet_out);
if (packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))
{
ctl->sc_flags |= SC_LOST_ACK;
@ -652,102 +657,110 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
{
struct lsquic_packets_tailq acked_acks =
TAILQ_HEAD_INITIALIZER(acked_acks);
#if !LSQUIC_CAN_REORDER
const struct lsquic_packno_range *range =
&acki->ranges[ acki->n_ranges - 1 ];
#endif
lsquic_packet_out_t *packet_out, *next;
lsquic_time_t now = lsquic_time_now();
lsquic_packno_t high;
int rtt_updated = 0;
lsquic_time_t now = 0;
lsquic_packno_t smallest_unacked;
lsquic_packno_t ack2ed[2];
unsigned packet_sz;
int app_limited;
unsigned n;
signed char do_rtt, skip_checks;
LSQ_DEBUG("Got ACK frame, largest acked: %"PRIu64"; delta: %"PRIu64,
largest_acked(acki), acki->lack_delta);
packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
#if __GNUC__
__builtin_prefetch(packet_out);
#endif
#if __GNUC__
# define UNLIKELY(cond) __builtin_expect(cond, 0)
#else
# define UNLIKELY(cond) cond
#endif
#if __GNUC__
if (UNLIKELY(LSQ_LOG_ENABLED(LSQ_LOG_DEBUG)))
#endif
LSQ_DEBUG("Got ACK frame, largest acked: %"PRIu64"; delta: %"PRIu64,
largest_acked(acki), acki->lack_delta);
/* Validate ACK first: */
for (n = 0; n < acki->n_ranges; ++n)
if (!lsquic_senhist_sent_range(&ctl->sc_senhist, acki->ranges[n].low,
acki->ranges[n].high))
{
LSQ_INFO("at least one packet in ACK range [%"PRIu64" - %"PRIu64"] "
"was never sent", acki->ranges[n].low, acki->ranges[n].high);
return -1;
}
if (UNLIKELY(largest_acked(acki)
> lsquic_senhist_largest(&ctl->sc_senhist)))
{
LSQ_INFO("at least one packet in ACK range [%"PRIu64" - %"PRIu64"] "
"was never sent", acki->ranges[0].low, acki->ranges[0].high);
return -1;
}
if (ctl->sc_flags & SC_WAS_QUIET)
if (UNLIKELY(ctl->sc_flags & SC_WAS_QUIET))
{
ctl->sc_flags &= ~SC_WAS_QUIET;
LSQ_DEBUG("ACK comes after a period of quiescence");
if (!now)
now = lsquic_time_now();
lsquic_cubic_was_quiet(&ctl->sc_cubic, now);
}
/* Peer is acking packets that have been acked already. Schedule ACK
* and STOP_WAITING frame to chop the range if we get two of these in
* a row.
*/
if (lsquic_send_ctl_smallest_unacked(ctl) > smallest_acked(acki))
++ctl->sc_n_stop_waiting;
else
ctl->sc_n_stop_waiting = 0;
if (UNLIKELY(!packet_out))
goto no_unacked_packets;
app_limited = send_ctl_retx_bytes_out(ctl) + 3 * ctl->sc_pack_size /* This
is the "maximum burst" parameter */
< lsquic_cubic_get_cwnd(&ctl->sc_cubic);
smallest_unacked = packet_out->po_packno;
ack2ed[1] = 0;
for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets);
packet_out
#if !LSQUIC_CAN_REORDER
&& packet_out->po_packno <= largest_acked(acki)
#endif
;
packet_out = next)
if (packet_out->po_packno > largest_acked(acki))
goto detect_losses;
do_rtt = 0, skip_checks = 0;
app_limited = -1;
do
{
next = TAILQ_NEXT(packet_out, po_next);
#if LSQUIC_CAN_REORDER
if (!in_acked_range(acki, packet_out->po_packno))
continue;
#else
/* This is faster than binary search in the normal case when the number
* of ranges is not much larger than the number of unacked packets.
*/
while (range->high < packet_out->po_packno)
--range;
if (range->low > packet_out->po_packno)
continue;
#endif
ctl->sc_largest_acked_packno = packet_out->po_packno;
ctl->sc_largest_acked_sent_time = packet_out->po_sent;
if (packet_out->po_packno == largest_acked(acki))
{
take_rtt_sample(ctl, packet_out, ack_recv_time, acki->lack_delta);
++rtt_updated;
}
lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
app_limited, lsquic_packet_out_total_sz(packet_out));
LSQ_DEBUG("Got ACK for packet %"PRIu64", remove from unacked queue",
packet_out->po_packno);
assert(ctl->sc_n_in_flight_all);
send_ctl_unacked_remove(ctl, packet_out);
lsquic_packet_out_ack_streams(packet_out);
#if __GNUC__
__builtin_prefetch(next);
#endif
if ((ctl->sc_flags & SC_NSTP) &&
(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK)))
TAILQ_INSERT_TAIL(&acked_acks, packet_out, po_next);
else
if (skip_checks)
goto after_checks;
/* This is faster than binary search in the normal case when the number
* of ranges is not much larger than the number of unacked packets.
*/
while (UNLIKELY(range->high < packet_out->po_packno))
--range;
if (range->low <= packet_out->po_packno)
{
skip_checks = range == acki->ranges;
if (app_limited < 0)
app_limited = send_ctl_retx_bytes_out(ctl) + 3 * ctl->sc_pack_size /* This
is the "maximum burst" parameter */
< lsquic_cubic_get_cwnd(&ctl->sc_cubic);
if (!now)
now = lsquic_time_now();
after_checks:
packet_sz = lsquic_packet_out_sent_sz(packet_out);
ctl->sc_largest_acked_packno = packet_out->po_packno;
ctl->sc_largest_acked_sent_time = packet_out->po_sent;
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
ack2ed[!!(packet_out->po_frame_types & (1 << QUIC_FRAME_ACK))]
= packet_out->po_ack2ed;
do_rtt |= packet_out->po_packno == largest_acked(acki);
lsquic_cubic_ack(&ctl->sc_cubic, now, now - packet_out->po_sent,
app_limited, packet_sz);
lsquic_packet_out_ack_streams(packet_out);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
packet_out = next;
}
while (packet_out && packet_out->po_packno <= largest_acked(acki));
if (rtt_updated)
if (do_rtt)
{
take_rtt_sample(ctl, ack_recv_time, acki->lack_delta);
ctl->sc_n_consec_rtos = 0;
ctl->sc_n_hsk = 0;
ctl->sc_n_tlp = 0;
}
detect_losses:
send_ctl_detect_losses(ctl, ack_recv_time);
if (send_ctl_first_unacked_retx_packet(ctl))
set_retx_alarm(ctl);
@ -758,29 +771,28 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
}
lsquic_send_ctl_sanity_check(ctl);
/* Processing of packets that contain acked ACK frames is deferred because
* we only need to process one of them: the last one, which we know to
* contain the largest value.
*/
packet_out = TAILQ_LAST(&acked_acks, lsquic_packets_tailq);
if (packet_out)
{
high = ctl->sc_conn_pub->lconn->cn_pf->pf_parse_ack_high(
packet_out->po_data, packet_out->po_data_sz);
if (high > ctl->sc_largest_ack2ed)
ctl->sc_largest_ack2ed = high;
do
{
next = TAILQ_PREV(packet_out, lsquic_packets_tailq, po_next);
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
while ((packet_out = next));
}
if ((ctl->sc_flags & SC_NSTP) && ack2ed[1] > ctl->sc_largest_ack2ed)
ctl->sc_largest_ack2ed = ack2ed[1];
if (ctl->sc_n_in_flight_retx == 0)
ctl->sc_flags |= SC_WAS_QUIET;
update_n_stop_waiting:
if (smallest_unacked > smallest_acked(acki))
/* Peer is acking packets that have been acked already. Schedule ACK
* and STOP_WAITING frame to chop the range if we get two of these in
* a row.
*/
++ctl->sc_n_stop_waiting;
else
ctl->sc_n_stop_waiting = 0;
lsquic_send_ctl_sanity_check(ctl);
return 0;
no_unacked_packets:
smallest_unacked = lsquic_senhist_largest(&ctl->sc_senhist) + 1;
ctl->sc_flags |= SC_WAS_QUIET;
goto update_n_stop_waiting;
}
@ -793,7 +805,7 @@ lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl)
* on purpose). Thus, the first packet on the unacked packets list has
* the smallest packet number of all packets on that list.
*/
if ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
if ((packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets)))
return packet_out->po_packno;
else
return lsquic_senhist_largest(&ctl->sc_senhist) + 1;
@ -966,7 +978,7 @@ void
lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
{
const struct lsquic_packet_out *packet_out;
unsigned count, sched_bytes;
unsigned count, bytes;
assert(!send_ctl_first_unacked_retx_packet(ctl) ||
lsquic_alarmset_is_set(ctl->sc_alset, AL_RETX));
@ -976,20 +988,24 @@ lsquic_send_ctl_sanity_check (const lsquic_send_ctl_t *ctl)
assert(lsquic_time_now() < ctl->sc_alset->as_expiry[AL_RETX] + MAX_RTO_DELAY);
}
count = 0;
count = 0, bytes = 0;
TAILQ_FOREACH(packet_out, &ctl->sc_unacked_packets, po_next)
{
bytes += lsquic_packet_out_sent_sz(packet_out);
++count;
}
assert(count == ctl->sc_n_in_flight_all);
assert(bytes == ctl->sc_bytes_unacked_all);
count = 0, sched_bytes = 0;
count = 0, bytes = 0;
TAILQ_FOREACH(packet_out, &ctl->sc_scheduled_packets, po_next)
{
assert(packet_out->po_flags & PO_SCHED);
sched_bytes += lsquic_packet_out_total_sz(packet_out);
bytes += lsquic_packet_out_total_sz(packet_out);
++count;
}
assert(count == ctl->sc_n_scheduled);
assert(sched_bytes == ctl->sc_bytes_scheduled);
assert(bytes == ctl->sc_bytes_scheduled);
}
@ -1204,6 +1220,7 @@ update_for_resending (lsquic_send_ctl_t *ctl, lsquic_packet_out_t *packet_out)
oldno = packet_out->po_packno;
packno = send_ctl_next_packno(ctl);
packet_out->po_flags &= ~PO_SENT_SZ;
packet_out->po_frame_types &= ~QFRAME_REGEN_MASK;
assert(packet_out->po_frame_types);
packet_out->po_packno = packno;
@ -1284,9 +1301,11 @@ lsquic_send_ctl_set_tcid0 (lsquic_send_ctl_t *ctl, int tcid0)
void
lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
{
struct lsquic_packet_out *packet_out = NULL, *next = NULL;
struct lsquic_packet_out *packet_out, *next = NULL;
struct lsquic_packet_out *pre_dropped;
unsigned n, adj;
pre_dropped = NULL;
for (packet_out = TAILQ_FIRST(&ctl->sc_scheduled_packets); packet_out;
packet_out = next)
{
@ -1300,6 +1319,9 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
ctl->sc_bytes_scheduled -= adj;
if (0 == packet_out->po_frame_types)
{
if (!pre_dropped)
pre_dropped = TAILQ_PREV(packet_out, lsquic_packets_tailq,
po_next);
LSQ_DEBUG("cancel packet %"PRIu64" after eliding frames for "
"stream %"PRIu32, packet_out->po_packno, stream_id);
send_ctl_sched_remove(ctl, packet_out);
@ -1308,6 +1330,21 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
}
}
/* Need to assign new packet numbers to all packets following the first
* dropped packet to eliminate packet number gap.
*/
if (pre_dropped)
{
ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
for (packet_out = TAILQ_NEXT(pre_dropped, po_next); packet_out;
packet_out = TAILQ_NEXT(packet_out, po_next))
{
packet_out->po_flags |= PO_REPACKNO;
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_release_enc_data(ctl, packet_out);
}
}
for (n = 0; n < sizeof(ctl->sc_buffered_packets) /
sizeof(ctl->sc_buffered_packets[0]); ++n)
{
@ -1339,7 +1376,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
* packets.
*/
#ifndef NDEBUG
#if __GNUC__
#if __GNUC__
__attribute__((weak))
#endif
#endif
@ -1420,12 +1457,7 @@ lsquic_send_ctl_squeeze_sched (lsquic_send_ctl_t *ctl)
if (packet_out->po_regen_sz < packet_out->po_data_sz)
{
if (packet_out->po_flags & PO_ENCRYPTED)
{
ctl->sc_enpub->enp_pmi->pmi_release(ctl->sc_enpub->enp_pmi_ctx,
packet_out->po_enc_data);
packet_out->po_enc_data = NULL;
packet_out->po_flags &= ~PO_ENCRYPTED;
}
send_ctl_release_enc_data(ctl, packet_out);
}
else
{
@ -1490,6 +1522,7 @@ lsquic_send_ctl_drop_scheduled (lsquic_send_ctl_t *ctl)
lsquic_packet_out_destroy(packet_out, ctl->sc_enpub);
}
assert(0 == ctl->sc_n_scheduled);
ctl->sc_cur_packno = lsquic_senhist_largest(&ctl->sc_senhist);
LSQ_DEBUG("dropped %u scheduled packet%s", n, n != 0 ? "s" : "");
}

View file

@ -28,23 +28,51 @@ struct buf_packet_q
unsigned bpq_count;
};
enum send_ctl_flags {
SC_TCID0 = (1 << 0),
SC_LOST_ACK = (1 << 1),
SC_NSTP = (1 << 2),
SC_PACE = (1 << 3),
SC_SCHED_TICK = (1 << 4),
SC_BUFFER_STREAM= (1 << 5),
SC_WAS_QUIET = (1 << 6),
};
typedef struct lsquic_send_ctl {
/* The first section consists of struct members which are used in the
* time-critical lsquic_send_ctl_got_ack() in the approximate order
* of usage.
*/
lsquic_senhist_t sc_senhist;
enum send_ctl_flags sc_flags;
unsigned sc_n_stop_waiting;
struct lsquic_packets_tailq sc_unacked_packets;
lsquic_packno_t sc_largest_acked_packno;
lsquic_time_t sc_largest_acked_sent_time;
unsigned sc_bytes_out;
unsigned sc_bytes_unacked_retx;
unsigned sc_bytes_scheduled;
unsigned sc_pack_size;
struct lsquic_cubic sc_cubic;
struct lsquic_engine_public *sc_enpub;
unsigned sc_bytes_unacked_all;
unsigned sc_n_in_flight_all;
unsigned sc_n_in_flight_retx;
unsigned sc_n_consec_rtos;
unsigned sc_n_hsk;
unsigned sc_n_tlp;
struct lsquic_alarmset *sc_alset;
/* Second section: everything else. */
struct lsquic_packets_tailq sc_scheduled_packets,
sc_unacked_packets,
sc_lost_packets;
struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1];
struct lsquic_engine_public *sc_enpub;
struct lsquic_alarmset *sc_alset;
const struct ver_neg *sc_ver_neg;
struct lsquic_conn_public *sc_conn_pub;
struct lsquic_cubic sc_cubic;
struct pacer sc_pacer;
lsquic_senhist_t sc_senhist;
lsquic_packno_t sc_cur_packno;
lsquic_packno_t sc_largest_sent_at_cutback;
lsquic_packno_t sc_max_rtt_packno;
lsquic_packno_t sc_largest_acked_packno;
lsquic_time_t sc_largest_acked_sent_time;
/* sc_largest_ack2ed is the packet number sent by peer that we acked and
* we know that our ACK was received by peer. This is used to determine
* the receive history cutoff point for the purposes of generating ACK
@ -59,28 +87,8 @@ typedef struct lsquic_send_ctl {
uint32_t stream_id;
enum buf_packet_type packet_type;
} sc_cached_bpt;
unsigned sc_bytes_out;
unsigned sc_bytes_unacked_all;
unsigned sc_bytes_unacked_retx;
unsigned sc_n_consec_rtos;
unsigned sc_next_limit;
unsigned sc_n_in_flight_all;
unsigned sc_n_in_flight_retx;
unsigned sc_n_scheduled;
unsigned sc_bytes_scheduled;
unsigned sc_n_stop_waiting;
unsigned short sc_pack_size;
enum {
SC_TCID0 = (1 << 0),
SC_LOST_ACK = (1 << 1),
SC_NSTP = (1 << 2),
SC_PACE = (1 << 3),
SC_SCHED_TICK = (1 << 4),
SC_BUFFER_STREAM= (1 << 5),
SC_WAS_QUIET = (1 << 6),
} sc_flags:8;
unsigned char sc_n_hsk;
unsigned char sc_n_tlp;
#if LSQUIC_SEND_STATS
struct {
unsigned n_total_sent,

View file

@ -3,126 +3,18 @@
* lsquic_senhist.c -- Sent history implementation
*/
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lsquic_int_types.h"
#include "lsquic_senhist.h"
void
lsquic_senhist_init (lsquic_senhist_t *hist)
{
lsquic_packints_init(&hist->sh_pints);
}
void
lsquic_senhist_cleanup (lsquic_senhist_t *hist)
{
lsquic_packints_cleanup(&hist->sh_pints);
}
/* At the time of this writing, the only reason the sequence of sent
* packet numbers could contain a hole is elision of stream frames from
* scheduled, but delayed packets. If such packet becomes empty after
* elision, it is dropped from the queue.
*/
/* The fast insert is used in the normal case, when packets are sent
* out in the same order in which they are scheduled: that is, their
* packet numbers are always increasing.
*/
static int
senhist_add_fast (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
struct packet_interval *pi;
pi = TAILQ_FIRST(&hist->sh_pints.pk_intervals);
if (pi)
{
/* Check that packet numbers are always increasing */
assert(packno > pi->range.high);
if (packno == pi->range.high + 1)
{
++pi->range.high;
return 0;
}
}
pi = malloc(sizeof(*pi));
if (!pi)
return -1;
pi->range.high = packno;
pi->range.low = packno;
TAILQ_INSERT_HEAD(&hist->sh_pints.pk_intervals, pi, next_pi);
return 0;
}
int
lsquic_senhist_add (lsquic_senhist_t *hist, lsquic_packno_t packno)
{
return senhist_add_fast(hist, packno);
}
int
lsquic_senhist_sent_range (lsquic_senhist_t *hist, lsquic_packno_t low,
lsquic_packno_t high)
{
const struct lsquic_packno_range *range;
for (range = lsquic_packints_first(&hist->sh_pints); range;
range = lsquic_packints_next(&hist->sh_pints))
if (range->low <= low && range->high >= high)
return 1;
return 0;
}
lsquic_packno_t
lsquic_senhist_largest (lsquic_senhist_t *hist)
{
const struct lsquic_packno_range *range;
range = lsquic_packints_first(&hist->sh_pints);
if (range)
return range->high;
else
return 0;
}
void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz)
{
const struct lsquic_packno_range *range;
size_t off;
int n;
for (off = 0, range = lsquic_packints_first(&hist->sh_pints);
range && off < bufsz;
off += n, range = lsquic_packints_next(&hist->sh_pints))
{
n = snprintf(buf + off, bufsz - off, "[%"PRIu64"-%"PRIu64"]",
range->high, range->low);
if (n < 0 || (size_t) n >= bufsz - off)
break;
}
if (bufsz > 0)
buf[off] = '\0';
if (hist->sh_last_sent)
snprintf(buf, bufsz, "[1-%"PRIu64"]", hist->sh_last_sent);
else
snprintf(buf, bufsz, "[]");
}
size_t
lsquic_senhist_mem_used (const struct lsquic_senhist *hist)
{
return sizeof(*hist)
- sizeof(hist->sh_pints)
+ lsquic_packints_mem_used(&hist->sh_pints);
}

View file

@ -2,49 +2,61 @@
/*
* lsquic_senhist.h -- History sent packets.
*
* We only keep track of packet numbers in order to verify ACKs.
* We need to keep track of packet numbers in order to verify ACKs. To
* speed up processing, we make sure that there is never a gap in the
* packet number sequence we generate.
*/
#ifndef LSQUIC_SENHIST_H
#define LSQUIC_SENHIST_H 1
#include "lsquic_packints.h"
#ifndef LSQUIC_SENHIST_FATAL
# define LSQUIC_SENHIST_FATAL 0
#endif
typedef struct lsquic_senhist {
/* These ranges are ordered from high to low. While searching this
* structure is O(n), I expect that in practice, a very long search
* could only happen once before the connection is terminated,
* because:
* a) either the packet number far away is real, but it was so long
* ago that it would have timed out by now (RTO); or
* b) the peer sends an invalid ACK.
*/
struct packints sh_pints;
lsquic_packno_t sh_last_sent;
#if !LSQUIC_SENHIST_FATAL
enum {
SH_WARNED = 1 << 0, /* Warn once */
} sh_flags;
#endif
} lsquic_senhist_t;
void
lsquic_senhist_init (lsquic_senhist_t *);
#define lsquic_senhist_init(hist) do { \
(hist)->sh_last_sent = 0; \
} while (0)
void
lsquic_senhist_cleanup (lsquic_senhist_t *);
#define lsquic_senhist_cleanup(hist)
int
lsquic_senhist_add (lsquic_senhist_t *, lsquic_packno_t);
/* Returns true if history contains all packets numbers in this range.
*/
int
lsquic_senhist_sent_range (lsquic_senhist_t *, lsquic_packno_t low,
lsquic_packno_t high);
#if LSQUIC_SENHIST_FATAL
#define lsquic_senhist_add(hist, packno) do { \
assert((hist)->sh_last_sent == packno - 1); \
(hist)->sh_last_sent = packno; \
} while (0)
#else
#define lsquic_senhist_add(hist, packno) do { \
if ((hist)->sh_last_sent == packno - 1) \
(hist)->sh_last_sent = packno; \
else \
{ \
if (!((hist)->sh_flags & SH_WARNED)) \
{ \
LSQ_WARN("send history gap %"PRIu64" - %"PRIu64, \
(hist)->sh_last_sent, packno); \
(hist)->sh_flags |= SH_WARNED; \
} \
(hist)->sh_last_sent = packno; \
} \
} while (0)
#endif
/* Returns 0 if no packets have been sent yet */
lsquic_packno_t
lsquic_senhist_largest (lsquic_senhist_t *hist);
#define lsquic_senhist_largest(hist) (+(hist)->sh_last_sent)
void
lsquic_senhist_tostr (lsquic_senhist_t *hist, char *buf, size_t bufsz);
size_t
lsquic_senhist_mem_used (const struct lsquic_senhist *);
#define lsquic_senhist_mem_used(hist) (sizeof(*(hist)))
#endif