2 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 #include "internal/quic_stream_map.h"
11 #include "internal/nelem.h"
17 int ossl_quic_stream_stop_sending(QUIC_STREAM *s, uint64_t aec)
22 s->stop_sending_aec = aec;
24 s->want_stop_sending = 1;
28 int ossl_quic_stream_reset(QUIC_STREAM *s, uint64_t aec)
33 s->reset_stream_aec = aec;
35 s->want_reset_stream = 1;
43 DEFINE_LHASH_OF_EX(QUIC_STREAM);
45 /* Circular list management. */
46 static void list_insert_tail(QUIC_STREAM_LIST_NODE *l,
47 QUIC_STREAM_LIST_NODE *n)
49 /* Must not be in list. */
50 assert(n->prev == NULL && n->next == NULL);
58 static void list_remove(QUIC_STREAM_LIST_NODE *l,
59 QUIC_STREAM_LIST_NODE *n)
61 assert(n->prev != NULL && n->next != NULL
62 && n->prev != n && n->next != n);
64 n->prev->next = n->next;
65 n->next->prev = n->prev;
66 n->next = n->prev = NULL;
69 static QUIC_STREAM *list_next(QUIC_STREAM_LIST_NODE *l, QUIC_STREAM_LIST_NODE *n,
79 return (QUIC_STREAM *)(((char *)n) - off);
82 #define active_next(l, s) list_next((l), &(s)->active_node, \
83 offsetof(QUIC_STREAM, active_node))
84 #define accept_next(l, s) list_next((l), &(s)->accept_node, \
85 offsetof(QUIC_STREAM, accept_node))
86 #define accept_head(l) list_next((l), (l), \
87 offsetof(QUIC_STREAM, accept_node))
89 static unsigned long hash_stream(const QUIC_STREAM *s)
91 return (unsigned long)s->id;
94 static int cmp_stream(const QUIC_STREAM *a, const QUIC_STREAM *b)
103 int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
104 uint64_t (*get_stream_limit_cb)(int uni, void *arg),
105 void *get_stream_limit_cb_arg)
107 qsm->map = lh_QUIC_STREAM_new(hash_stream, cmp_stream);
108 qsm->active_list.prev = qsm->active_list.next = &qsm->active_list;
109 qsm->accept_list.prev = qsm->accept_list.next = &qsm->accept_list;
110 qsm->rr_stepping = 1;
115 qsm->get_stream_limit_cb = get_stream_limit_cb;
116 qsm->get_stream_limit_cb_arg = get_stream_limit_cb_arg;
120 static void release_each(QUIC_STREAM *stream, void *arg)
122 QUIC_STREAM_MAP *qsm = arg;
124 ossl_quic_stream_map_release(qsm, stream);
127 void ossl_quic_stream_map_cleanup(QUIC_STREAM_MAP *qsm)
129 ossl_quic_stream_map_visit(qsm, release_each, qsm);
131 lh_QUIC_STREAM_free(qsm->map);
135 void ossl_quic_stream_map_visit(QUIC_STREAM_MAP *qsm,
136 void (*visit_cb)(QUIC_STREAM *stream, void *arg),
139 lh_QUIC_STREAM_doall_arg(qsm->map, visit_cb, visit_cb_arg);
142 QUIC_STREAM *ossl_quic_stream_map_alloc(QUIC_STREAM_MAP *qsm,
151 s = lh_QUIC_STREAM_retrieve(qsm->map, &key);
155 s = OPENSSL_zalloc(sizeof(*s));
161 lh_QUIC_STREAM_insert(qsm->map, s);
165 void ossl_quic_stream_map_release(QUIC_STREAM_MAP *qsm, QUIC_STREAM *stream)
170 ossl_quic_sstream_free(stream->sstream);
171 stream->sstream = NULL;
173 ossl_quic_rstream_free(stream->rstream);
174 stream->rstream = NULL;
176 lh_QUIC_STREAM_delete(qsm->map, stream);
177 OPENSSL_free(stream);
180 QUIC_STREAM *ossl_quic_stream_map_get_by_id(QUIC_STREAM_MAP *qsm,
187 return lh_QUIC_STREAM_retrieve(qsm->map, &key);
190 static void stream_map_mark_active(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
195 list_insert_tail(&qsm->active_list, &s->active_node);
197 if (qsm->rr_cur == NULL)
203 static void stream_map_mark_inactive(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
208 list_remove(&qsm->active_list, &s->active_node);
210 if (qsm->rr_cur == s)
211 qsm->rr_cur = active_next(&qsm->active_list, s);
212 if (qsm->rr_cur == s)
218 void ossl_quic_stream_map_set_rr_stepping(QUIC_STREAM_MAP *qsm, size_t stepping)
220 qsm->rr_stepping = stepping;
224 static int stream_has_data_to_send(QUIC_STREAM *s)
226 OSSL_QUIC_FRAME_STREAM shdr;
227 OSSL_QTX_IOVEC iov[2];
229 uint64_t fc_credit, fc_swm, fc_limit;
231 if (s->sstream == NULL)
235 * We cannot determine if we have data to send simply by checking if
236 * ossl_quic_txfc_get_credit() is zero, because we may also have older
237 * stream data we need to retransmit. The SSTREAM returns older data first,
238 * so we do a simple comparison of the next chunk the SSTREAM wants to send
239 * against the TXFC CWM.
241 num_iov = OSSL_NELEM(iov);
242 if (!ossl_quic_sstream_get_stream_frame(s->sstream, 0, &shdr, iov,
246 fc_credit = ossl_quic_txfc_get_credit(&s->txfc);
247 fc_swm = ossl_quic_txfc_get_swm(&s->txfc);
248 fc_limit = fc_swm + fc_credit;
250 return (shdr.is_fin && shdr.len == 0) || shdr.offset < fc_limit;
253 void ossl_quic_stream_map_update_state(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
255 int should_be_active, allowed_by_stream_limit = 1;
257 if (qsm->get_stream_limit_cb != NULL
258 && (s->type & QUIC_STREAM_INITIATOR_CLIENT) != 0) {
259 int uni = ((s->type & QUIC_STREAM_DIR_UNI) != 0);
260 uint64_t stream_limit, stream_ordinal = s->id >> 2;
263 = qsm->get_stream_limit_cb(uni, qsm->get_stream_limit_cb_arg);
265 allowed_by_stream_limit = (stream_ordinal < stream_limit);
269 = allowed_by_stream_limit
270 && !s->peer_stop_sending
271 && !s->peer_reset_stream
272 && ((s->rstream != NULL
273 && (s->want_max_stream_data
274 || ossl_quic_rxfc_has_cwm_changed(&s->rxfc, 0)))
275 || s->want_stop_sending
276 || s->want_reset_stream
277 || stream_has_data_to_send(s));
279 if (should_be_active)
280 stream_map_mark_active(qsm, s);
282 stream_map_mark_inactive(qsm, s);
285 QUIC_STREAM *ossl_quic_stream_map_peek_accept_queue(QUIC_STREAM_MAP *qsm)
287 return accept_head(&qsm->accept_list);
290 void ossl_quic_stream_map_push_accept_queue(QUIC_STREAM_MAP *qsm,
293 list_insert_tail(&qsm->accept_list, &s->accept_node);
297 void ossl_quic_stream_map_remove_from_accept_queue(QUIC_STREAM_MAP *qsm,
300 list_remove(&qsm->accept_list, &s->accept_node);
304 size_t ossl_quic_stream_map_get_accept_queue_len(QUIC_STREAM_MAP *qsm)
306 return qsm->num_accept;
310 * QUIC Stream Iterator
311 * ====================
313 void ossl_quic_stream_iter_init(QUIC_STREAM_ITER *it, QUIC_STREAM_MAP *qsm,
317 it->stream = it->first_stream = qsm->rr_cur;
318 if (advance_rr && it->stream != NULL
319 && ++qsm->rr_counter >= qsm->rr_stepping) {
321 qsm->rr_cur = active_next(&qsm->active_list, qsm->rr_cur);
325 void ossl_quic_stream_iter_next(QUIC_STREAM_ITER *it)
327 if (it->stream == NULL)
330 it->stream = active_next(&it->qsm->active_list, it->stream);
331 if (it->stream == it->first_stream)