diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c index a4e0882b278c3e9ad90fd2a4cd98570db6c58a79..28550ee5b06c6dddbdfa39ad90b8e4c2c7bc234b 100644 --- a/lib/ngtcp2_conn.c +++ b/lib/ngtcp2_conn.c @@ -8319,8 +8319,10 @@ ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn); ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn); ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn); + ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn); ngtcp2_tstamp res = ngtcp2_min(t1, t2); - return ngtcp2_min(res, t3); + res = ngtcp2_min(res, t3); + return ngtcp2_min(res, t4); } int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { @@ -8328,6 +8330,8 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); + ngtcp2_conn_remove_lost_pkt(conn, ts); + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); if (rv != 0) { @@ -8357,6 +8361,49 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); } +ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { + ngtcp2_tstamp res = UINT64_MAX, ts; + + if (conn->in_pktns) { + ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb); + if (ts != UINT64_MAX) { + ts += conn_compute_pto(conn, conn->in_pktns); + res = ngtcp2_min(res, ts); + } + } + + if (conn->hs_pktns) { + ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb); + if (ts != UINT64_MAX) { + ts += conn_compute_pto(conn, conn->hs_pktns); + res = ngtcp2_min(res, ts); + } + } + + ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb); + if (ts != UINT64_MAX) { + ts += conn_compute_pto(conn, &conn->pktns); + res = ngtcp2_min(res, ts); + } + + return res; +} + +void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_tstamp pto; + + if (conn->in_pktns) { + pto = conn_compute_pto(conn, conn->in_pktns); + ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts); + } + if (conn->hs_pktns) { + pto = conn_compute_pto(conn, conn->hs_pktns); + ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts); + } + pto = conn_compute_pto(conn, &conn->pktns); + ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); +} + /* * conn_client_validate_transport_params validates |params| as client. * |params| must be sent with Encrypted Extensions. diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h index 1c0dc3ac448ecec9edac56dafa26bdb1c88b8456..fcc7860c052bdaf3d8caf65dc670a265d87f7cc8 100644 --- a/lib/ngtcp2_conn.h +++ b/lib/ngtcp2_conn.h @@ -638,4 +638,15 @@ ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, */ int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn); +/* + * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of + * lost packet. + */ +ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_remove_lost_pkt removes the expired lost packet. + */ +void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts); + #endif /* NGTCP2_CONN_H */ diff --git a/lib/ngtcp2_rtb.c b/lib/ngtcp2_rtb.c index f55e18d62debd75f338ee2b1cd17a0c3c9f8291f..0f1d97f27d4964e66d00f018771de4084593266d 100644 --- a/lib/ngtcp2_rtb.c +++ b/lib/ngtcp2_rtb.c @@ -840,33 +840,65 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, } } + /* TODO Limit the number of LOST_RETRANSMITTED entries under maximum + reordering threshold. */ + + return 0; +} + +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return 0; + return; } it = ngtcp2_ksl_end(&rtb->ents); for (;;) { + assert(ngtcp2_ksl_it_end(&it)); + ngtcp2_ksl_it_prev(&it); ent = ngtcp2_ksl_it_get(&it); if (!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) || ts - ent->lost_ts < pto) { - return 0; + return; } ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, - "removing stale lost packet pkn=%" PRId64, ent->hd.pkt_num); + "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); ngtcp2_rtb_entry_del(ent, rtb->mem); if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return 0; + return; } } } +ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb) { + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + + if (ngtcp2_ksl_len(&rtb->ents) == 0) { + return UINT64_MAX; + } + + it = ngtcp2_ksl_end(&rtb->ents); + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + if (!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)) { + return UINT64_MAX; + } + + return ent->lost_ts; +} + static void rtb_on_pkt_lost2(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, ngtcp2_rtb_entry *ent) { ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, diff --git a/lib/ngtcp2_rtb.h b/lib/ngtcp2_rtb.h index c1a12d0278d2e17bc376b60340a9ed4775ba7898..b9f350fa857accb081b62610259bab3aa60b7f04 100644 --- a/lib/ngtcp2_rtb.h +++ b/lib/ngtcp2_rtb.h @@ -321,6 +321,19 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, ngtcp2_duration pto, ngtcp2_tstamp ts); +/* + * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. + */ +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_lost_pkt_ts returns the earliest time when the still + * retained packet was lost. It returns UINT64_MAX if no such packet + * exists. + */ +ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb); + /* * ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends * all frames to |*pfrc|. Even when this function fails, some frames diff --git a/tests/main.c b/tests/main.c index 5ffc4c7f92e67e8358fe88dd9966d7e84e8dd57c..3ac72fc1c92a86ce46adf71c233bc6ac949b00b1 100644 --- a/tests/main.c +++ b/tests/main.c @@ -166,6 +166,9 @@ int main() { test_ngtcp2_encode_transport_params) || !CU_add_test(pSuite, "rtb_add", test_ngtcp2_rtb_add) || !CU_add_test(pSuite, "rtb_recv_ack", test_ngtcp2_rtb_recv_ack) || + !CU_add_test(pSuite, "rtb_lost_pkt_ts", test_ngtcp2_rtb_lost_pkt_ts) || + !CU_add_test(pSuite, "rtb_remove_expired_lost_pkt", + test_ngtcp2_rtb_remove_expired_lost_pkt) || !CU_add_test(pSuite, "idtr_open", test_ngtcp2_idtr_open) || !CU_add_test(pSuite, "ringbuf_push_front", test_ngtcp2_ringbuf_push_front) || diff --git a/tests/ngtcp2_rtb_test.c b/tests/ngtcp2_rtb_test.c index 57e216f6007d659d0c8ea9c84dbea04dbcf8ac7e..3e303da36d37ec19f287b46af316bef9ebf8ab97 100644 --- a/tests/ngtcp2_rtb_test.c +++ b/tests/ngtcp2_rtb_test.c @@ -24,6 +24,8 @@ */ #include "ngtcp2_rtb_test.h" +#include <assert.h> + #include <CUnit/CUnit.h> #include "ngtcp2_rtb.h" @@ -283,3 +285,85 @@ void test_ngtcp2_rtb_recv_ack(void) { ngtcp2_cc_reno_cc_free(&cc, mem); ngtcp2_strm_free(&crypto); } + +void test_ngtcp2_rtb_lost_pkt_ts(void) { + ngtcp2_rtb rtb; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APP; + ngtcp2_strm crypto; + ngtcp2_log log; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cc cc; + ngtcp2_rst rst; + ngtcp2_conn_stat cstat; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, mem); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, mem); + + add_rtb_entry_range(&rtb, 0, 1, &cstat, mem); + + CU_ASSERT(UINT64_MAX == ngtcp2_rtb_lost_pkt_ts(&rtb)); + + it = ngtcp2_ksl_end(&rtb.ents); + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + ent->flags |= NGTCP2_RTB_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = 16777217; + + CU_ASSERT(16777217 == ngtcp2_rtb_lost_pkt_ts(&rtb)); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); +} + +void test_ngtcp2_rtb_remove_expired_lost_pkt(void) { + ngtcp2_rtb rtb; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APP; + ngtcp2_strm crypto; + ngtcp2_log log; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cc cc; + ngtcp2_rst rst; + ngtcp2_conn_stat cstat; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + size_t i; + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, mem); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, mem); + + add_rtb_entry_range(&rtb, 0, 7, &cstat, mem); + + it = ngtcp2_ksl_end(&rtb.ents); + + for (i = 0; i < 5; ++i) { + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + ent->flags |= NGTCP2_RTB_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = 16777217 + i; + } + + ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777219); + + CU_ASSERT(5 == ngtcp2_ksl_len(&rtb.ents)); + + ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777223); + + CU_ASSERT(2 == ngtcp2_ksl_len(&rtb.ents)); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); +} diff --git a/tests/ngtcp2_rtb_test.h b/tests/ngtcp2_rtb_test.h index 3c890c249f5312694fb84e86c5a18c391d2f73b1..c9bf2f9671973f3fbd34939a9922809896756ac1 100644 --- a/tests/ngtcp2_rtb_test.h +++ b/tests/ngtcp2_rtb_test.h @@ -31,5 +31,7 @@ void test_ngtcp2_rtb_add(void); void test_ngtcp2_rtb_recv_ack(void); +void test_ngtcp2_rtb_lost_pkt_ts(void); +void test_ngtcp2_rtb_remove_expired_lost_pkt(void); #endif /* NGTCP2_RTB_TEST_H */