Re: [PATCH net-next v3 06/15] quic: add stream management
Xin Long <[email protected]> Tue, 23 Sep 2025 13:57:21 -0400
| Newsgroups | dev.linux.lists.quic,dev.linux.lists.kernel-tls-handshake,org.kernel.vger.linux-cifs,org.kernel.vger.netdev |
|---|---|
| Message-ID | <CADvbK_fE1KHbgtZV4zY1xXo94avimgxBcoakyoAYPOsb-U3rSw@mail.gmail.com> |
On Tue, Sep 23, 2025 at 9:39 AM Paolo Abeni <[email protected]> wrote: > > On 9/19/25 12:34 AM, Xin Long wrote: > > +/* Create and register new streams for sending. */ > > +static struct quic_stream *quic_stream_send_create(struct quic_stream_table *streams, > > + s64 max_stream_id, u8 is_serv) > > +{ > > + struct quic_stream *stream; > > + s64 stream_id; > > + > > + stream_id = streams->send.next_bidi_stream_id; > > + if (quic_stream_id_uni(max_stream_id)) > > + stream_id = streams->send.next_uni_stream_id; > > + > > + /* rfc9000#section-2.1: A stream ID that is used out of order results in all streams > > + * of that type with lower-numbered stream IDs also being opened. > > + */ > > + while (stream_id <= max_stream_id) { > > + stream = kzalloc(sizeof(*stream), GFP_KERNEL); > > + if (!stream) > > + return NULL; > > How many streams and connections ID do you foresee per socket? Could > such number grow significantly (possibly under misuse/attack)? If so you > you likely use GFP_KERNEL_ACCOUNT here (and in conn_id allocation). connections ID: 8 (QUIC_CONN_ID_LIMIT) at most per socket. streams: 4096 (QUIC_MAX_STREAMS) at most per socket. I can switch to GFP_KERNEL_ACCOUNT in quic_stream_send_create(). For quic_stream_recv_create(), it’s typically invoked in atomic context. Since there’s no predefined GFP_ATOMIC_ACCOUNT, I assume using (GFP_ATOMIC | __GFP_ACCOUNT) should be acceptable. Thanks. > > /P > > > + > > + stream->id = stream_id; > > + if (quic_stream_id_uni(stream_id)) { > > + stream->send.max_bytes = streams->send.max_stream_data_uni; > > + > > + if (streams->send.next_uni_stream_id < stream_id + QUIC_STREAM_ID_STEP) > > + streams->send.next_uni_stream_id = stream_id + QUIC_STREAM_ID_STEP; > > + streams->send.streams_uni++; > > + > > + quic_stream_add(streams, stream); > > + stream_id += QUIC_STREAM_ID_STEP; > > + continue; > > + } > > + > > + if (streams->send.next_bidi_stream_id < stream_id + QUIC_STREAM_ID_STEP) > > + streams->send.next_bidi_stream_id = stream_id + QUIC_STREAM_ID_STEP; > > + streams->send.streams_bidi++; > > + > > + if (quic_stream_id_local(stream_id, is_serv)) { > > + stream->send.max_bytes = streams->send.max_stream_data_bidi_remote; > > + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_local; > > + } else { > > + stream->send.max_bytes = streams->send.max_stream_data_bidi_local; > > + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_remote; > > + } > > + stream->recv.window = stream->recv.max_bytes; > > + > > + quic_stream_add(streams, stream); > > + stream_id += QUIC_STREAM_ID_STEP; > > + } > > + return stream; > > +} > > + > > +/* Create and register new streams for receiving. */ > > +static struct quic_stream *quic_stream_recv_create(struct quic_stream_table *streams, > > + s64 max_stream_id, u8 is_serv) > > +{ > > + struct quic_stream *stream; > > + s64 stream_id; > > + > > + stream_id = streams->recv.next_bidi_stream_id; > > + if (quic_stream_id_uni(max_stream_id)) > > + stream_id = streams->recv.next_uni_stream_id; > > + > > + /* rfc9000#section-2.1: A stream ID that is used out of order results in all streams > > + * of that type with lower-numbered stream IDs also being opened. > > + */ > > + while (stream_id <= max_stream_id) { > > + stream = kzalloc(sizeof(*stream), GFP_ATOMIC); > > + if (!stream) > > + return NULL; > > + > > + stream->id = stream_id; > > + if (quic_stream_id_uni(stream_id)) { > > + stream->recv.window = streams->recv.max_stream_data_uni; > > + stream->recv.max_bytes = stream->recv.window; > > + > > + if (streams->recv.next_uni_stream_id < stream_id + QUIC_STREAM_ID_STEP) > > + streams->recv.next_uni_stream_id = stream_id + QUIC_STREAM_ID_STEP; > > + streams->recv.streams_uni++; > > + > > + quic_stream_add(streams, stream); > > + stream_id += QUIC_STREAM_ID_STEP; > > + continue; > > + } > > + > > + if (streams->recv.next_bidi_stream_id < stream_id + QUIC_STREAM_ID_STEP) > > + streams->recv.next_bidi_stream_id = stream_id + QUIC_STREAM_ID_STEP; > > + streams->recv.streams_bidi++; > > + > > + if (quic_stream_id_local(stream_id, is_serv)) { > > + stream->send.max_bytes = streams->send.max_stream_data_bidi_remote; > > + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_local; > > + } else { > > + stream->send.max_bytes = streams->send.max_stream_data_bidi_local; > > + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_remote; > > + } > > + stream->recv.window = stream->recv.max_bytes; > > + > > + quic_stream_add(streams, stream); > > + stream_id += QUIC_STREAM_ID_STEP; > > + } > > + return stream; > > +} > > + > > +/* Check if a send or receive stream ID is already closed. */ > > +static bool quic_stream_id_closed(struct quic_stream_table *streams, s64 stream_id, bool send) > > +{ > > + if (quic_stream_id_uni(stream_id)) { > > + if (send) > > + return stream_id < streams->send.next_uni_stream_id; > > + return stream_id < streams->recv.next_uni_stream_id; > > + } > > + if (send) > > + return stream_id < streams->send.next_bidi_stream_id; > > + return stream_id < streams->recv.next_bidi_stream_id; > > +} > > + > > +/* Check if a stream ID would exceed local (recv) or peer (send) limits. */ > > +bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id, bool send) > > +{ > > + u64 nstreams; > > + > > + if (!send) { > > + if (quic_stream_id_uni(stream_id)) > > + return stream_id > streams->recv.max_uni_stream_id; > > + return stream_id > streams->recv.max_bidi_stream_id; > > + } > > + > > + if (quic_stream_id_uni(stream_id)) { > > + if (stream_id > streams->send.max_uni_stream_id) > > + return true; > > + stream_id -= streams->send.next_uni_stream_id; > > + nstreams = quic_stream_id_to_streams(stream_id); > > + return nstreams + streams->send.streams_uni > streams->send.max_streams_uni; > > + } > > + > > + if (stream_id > streams->send.max_bidi_stream_id) > > + return true; > > + stream_id -= streams->send.next_bidi_stream_id; > > + nstreams = quic_stream_id_to_streams(stream_id); > > + return nstreams + streams->send.streams_bidi > streams->send.max_streams_bidi; > > +} > > + > > +/* Get or create a send stream by ID. */ > > +struct quic_stream *quic_stream_send_get(struct quic_stream_table *streams, s64 stream_id, > > + u32 flags, bool is_serv) > > +{ > > + struct quic_stream *stream; > > + > > + if (!quic_stream_id_valid(stream_id, is_serv, true)) > > + return ERR_PTR(-EINVAL); > > + > > + stream = quic_stream_find(streams, stream_id); > > + if (stream) { > > + if ((flags & MSG_STREAM_NEW) && > > + stream->send.state != QUIC_STREAM_SEND_STATE_READY) > > + return ERR_PTR(-EINVAL); > > + return stream; > > + } > > + > > + if (quic_stream_id_closed(streams, stream_id, true)) > > + return ERR_PTR(-ENOSTR); > > + > > + if (!(flags & MSG_STREAM_NEW)) > > + return ERR_PTR(-EINVAL); > > + > > + if (quic_stream_id_exceeds(streams, stream_id, true)) > > + return ERR_PTR(-EAGAIN); > > + > > + stream = quic_stream_send_create(streams, stream_id, is_serv); > > + if (!stream) > > + return ERR_PTR(-ENOSTR); > > + streams->send.active_stream_id = stream_id; > > + return stream; > > +} > > + > > +/* Get or create a receive stream by ID. */ > > +struct quic_stream *quic_stream_recv_get(struct quic_stream_table *streams, s64 stream_id, > > + bool is_serv) > > +{ > > + struct quic_stream *stream; > > + > > + if (!quic_stream_id_valid(stream_id, is_serv, false)) > > + return ERR_PTR(-EINVAL); > > + > > + stream = quic_stream_find(streams, stream_id); > > + if (stream) > > + return stream; > > + > > + if (quic_stream_id_local(stream_id, is_serv)) { > > + if (quic_stream_id_closed(streams, stream_id, true)) > > + return ERR_PTR(-ENOSTR); > > + return ERR_PTR(-EINVAL); > > + } > > + > > + if (quic_stream_id_closed(streams, stream_id, false)) > > + return ERR_PTR(-ENOSTR); > > + > > + if (quic_stream_id_exceeds(streams, stream_id, false)) > > + return ERR_PTR(-EAGAIN); > > + > > + stream = quic_stream_recv_create(streams, stream_id, is_serv); > > + if (!stream) > > + return ERR_PTR(-ENOSTR); > > + if (quic_stream_id_valid(stream_id, is_serv, true)) > > + streams->send.active_stream_id = stream_id; > > + return stream; > > +} > > + > > +/* Release or clean up a send stream. This function updates stream counters and state when > > + * a send stream has either successfully sent all data or has been reset. > > + */ > > +void quic_stream_send_put(struct quic_stream_table *streams, struct quic_stream *stream, > > + bool is_serv) > > +{ > > + if (quic_stream_id_uni(stream->id)) { > > + /* For unidirectional streams, decrement uni count and delete immediately. */ > > + streams->send.streams_uni--; > > + quic_stream_delete(stream); > > + return; > > + } > > + > > + /* For bidi streams, only proceed if receive side is in a final state. */ > > + if (stream->recv.state != QUIC_STREAM_RECV_STATE_RECVD && > > + stream->recv.state != QUIC_STREAM_RECV_STATE_READ && > > + stream->recv.state != QUIC_STREAM_RECV_STATE_RESET_RECVD) > > + return; > > + > > + if (quic_stream_id_local(stream->id, is_serv)) { > > + /* Local-initiated stream: mark send done and decrement send.bidi count. */ > > + if (!stream->send.done) { > > + stream->send.done = 1; > > + streams->send.streams_bidi--; > > + } > > + goto out; > > + } > > + /* Remote-initiated stream: mark recv done and decrement recv bidi count. */ > > + if (!stream->recv.done) { > > + stream->recv.done = 1; > > + streams->recv.streams_bidi--; > > + streams->recv.bidi_pending = 1; > > + } > > +out: > > + /* Delete stream if fully read or reset. */ > > + if (stream->recv.state != QUIC_STREAM_RECV_STATE_RECVD) > > + quic_stream_delete(stream); > > +} > > + > > +/* Release or clean up a receive stream. This function updates stream counters and state when > > + * the receive side has either consumed all data or has been reset. > > + */ > > +void quic_stream_recv_put(struct quic_stream_table *streams, struct quic_stream *stream, > > + bool is_serv) > > +{ > > + if (quic_stream_id_uni(stream->id)) { > > + /* For uni streams, decrement uni count and mark done. */ > > + if (!stream->recv.done) { > > + stream->recv.done = 1; > > + streams->recv.streams_uni--; > > + streams->recv.uni_pending = 1; > > + } > > + goto out; > > + } > > + > > + /* For bidi streams, only proceed if send side is in a final state. */ > > + if (stream->send.state != QUIC_STREAM_SEND_STATE_RECVD && > > + stream->send.state != QUIC_STREAM_SEND_STATE_RESET_RECVD) > > + return; > > + > > + if (quic_stream_id_local(stream->id, is_serv)) { > > + /* Local-initiated stream: mark send done and decrement send.bidi count. */ > > + if (!stream->send.done) { > > + stream->send.done = 1; > > + streams->send.streams_bidi--; > > + } > > + goto out; > > + } > > + /* Remote-initiated stream: mark recv done and decrement recv bidi count. */ > > + if (!stream->recv.done) { > > + stream->recv.done = 1; > > + streams->recv.streams_bidi--; > > + streams->recv.bidi_pending = 1; > > + } > > +out: > > + /* Delete stream if fully read or reset. */ > > + if (stream->recv.state != QUIC_STREAM_RECV_STATE_RECVD) > > + quic_stream_delete(stream); > > +} > > + > > +/* Updates the maximum allowed incoming stream IDs if any streams were recently closed. > > + * Recalculates the max_uni and max_bidi stream ID limits based on the number of open > > + * streams and whether any were marked for deletion. > > + * > > + * Returns true if either max_uni or max_bidi was updated, indicating that a > > + * MAX_STREAMS_UNI or MAX_STREAMS_BIDI frame should be sent to the peer. > > + */ > > +bool quic_stream_max_streams_update(struct quic_stream_table *streams, s64 *max_uni, s64 *max_bidi) > > +{ > > + if (streams->recv.uni_pending) { > > + streams->recv.max_uni_stream_id = > > + streams->recv.next_uni_stream_id - QUIC_STREAM_ID_STEP + > > + ((streams->recv.max_streams_uni - streams->recv.streams_uni) << > > + QUIC_STREAM_TYPE_BITS); > > + *max_uni = quic_stream_id_to_streams(streams->recv.max_uni_stream_id); > > + streams->recv.uni_pending = 0; > > + } > > + if (streams->recv.bidi_pending) { > > + streams->recv.max_bidi_stream_id = > > + streams->recv.next_bidi_stream_id - QUIC_STREAM_ID_STEP + > > + ((streams->recv.max_streams_bidi - streams->recv.streams_bidi) << > > + QUIC_STREAM_TYPE_BITS); > > + *max_bidi = quic_stream_id_to_streams(streams->recv.max_bidi_stream_id); > > + streams->recv.bidi_pending = 0; > > + } > > + > > + return *max_uni || *max_bidi; > > +} > > + > > +#define QUIC_STREAM_HT_SIZE 64 > > + > > +int quic_stream_init(struct quic_stream_table *streams) > > +{ > > + struct quic_shash_table *ht = &streams->ht; > > + int i, size = QUIC_STREAM_HT_SIZE; > > + struct quic_shash_head *head; > > + > > + head = kmalloc_array(size, sizeof(*head), GFP_KERNEL); > > + if (!head) > > + return -ENOMEM; > > + for (i = 0; i < size; i++) > > + INIT_HLIST_HEAD(&head[i].head); > > + ht->size = size; > > + ht->hash = head; > > + return 0; > > +} > > + > > +void quic_stream_free(struct quic_stream_table *streams) > > +{ > > + struct quic_shash_table *ht = &streams->ht; > > + struct quic_shash_head *head; > > + struct quic_stream *stream; > > + struct hlist_node *tmp; > > + int i; > > + > > + for (i = 0; i < ht->size; i++) { > > + head = &ht->hash[i]; > > + hlist_for_each_entry_safe(stream, tmp, &head->head, node) > > + quic_stream_delete(stream); > > + } > > + kfree(ht->hash); > > +} > > + > > +/* Populate transport parameters from stream hash table. */ > > +void quic_stream_get_param(struct quic_stream_table *streams, struct quic_transport_param *p, > > + bool is_serv) > > +{ > > + if (p->remote) { > > + p->max_stream_data_bidi_remote = streams->send.max_stream_data_bidi_remote; > > + p->max_stream_data_bidi_local = streams->send.max_stream_data_bidi_local; > > + p->max_stream_data_uni = streams->send.max_stream_data_uni; > > + p->max_streams_bidi = streams->send.max_streams_bidi; > > + p->max_streams_uni = streams->send.max_streams_uni; > > + return; > > + } > > + > > + p->max_stream_data_bidi_remote = streams->recv.max_stream_data_bidi_remote; > > + p->max_stream_data_bidi_local = streams->recv.max_stream_data_bidi_local; > > + p->max_stream_data_uni = streams->recv.max_stream_data_uni; > > + p->max_streams_bidi = streams->recv.max_streams_bidi; > > + p->max_streams_uni = streams->recv.max_streams_uni; > > +} > > + > > +/* Configure stream hashtable from transport parameters. */ > > +void quic_stream_set_param(struct quic_stream_table *streams, struct quic_transport_param *p, > > + bool is_serv) > > +{ > > + u8 type; > > + > > + if (p->remote) { > > + streams->send.max_stream_data_bidi_local = p->max_stream_data_bidi_local; > > + streams->send.max_stream_data_bidi_remote = p->max_stream_data_bidi_remote; > > + streams->send.max_stream_data_uni = p->max_stream_data_uni; > > + streams->send.max_streams_bidi = p->max_streams_bidi; > > + streams->send.max_streams_uni = p->max_streams_uni; > > + streams->send.active_stream_id = -1; > > + > > + if (is_serv) { > > + type = QUIC_STREAM_TYPE_SERVER_BIDI; > > + streams->send.max_bidi_stream_id = > > + quic_stream_streams_to_id(p->max_streams_bidi, type); > > + streams->send.next_bidi_stream_id = type; > > + > > + type = QUIC_STREAM_TYPE_SERVER_UNI; > > + streams->send.max_uni_stream_id = > > + quic_stream_streams_to_id(p->max_streams_uni, type); > > + streams->send.next_uni_stream_id = type; > > + return; > > + } > > + > > + type = QUIC_STREAM_TYPE_CLIENT_BIDI; > > + streams->send.max_bidi_stream_id = > > + quic_stream_streams_to_id(p->max_streams_bidi, type); > > + streams->send.next_bidi_stream_id = type; > > + > > + type = QUIC_STREAM_TYPE_CLIENT_UNI; > > + streams->send.max_uni_stream_id = > > + quic_stream_streams_to_id(p->max_streams_uni, type); > > + streams->send.next_uni_stream_id = type; > > + return; > > + } > > + > > + streams->recv.max_stream_data_bidi_local = p->max_stream_data_bidi_local; > > + streams->recv.max_stream_data_bidi_remote = p->max_stream_data_bidi_remote; > > + streams->recv.max_stream_data_uni = p->max_stream_data_uni; > > + streams->recv.max_streams_bidi = p->max_streams_bidi; > > + streams->recv.max_streams_uni = p->max_streams_uni; > > + > > + if (is_serv) { > > + type = QUIC_STREAM_TYPE_CLIENT_BIDI; > > + streams->recv.max_bidi_stream_id = > > + quic_stream_streams_to_id(p->max_streams_bidi, type); > > + streams->recv.next_bidi_stream_id = type; > > + > > + type = QUIC_STREAM_TYPE_CLIENT_UNI; > > + streams->recv.max_uni_stream_id = > > + quic_stream_streams_to_id(p->max_streams_uni, type); > > + streams->recv.next_uni_stream_id = type; > > + return; > > + } > > + > > + type = QUIC_STREAM_TYPE_SERVER_BIDI; > > + streams->recv.max_bidi_stream_id = > > + quic_stream_streams_to_id(p->max_streams_bidi, type); > > + streams->recv.next_bidi_stream_id = type; > > + > > + type = QUIC_STREAM_TYPE_SERVER_UNI; > > + streams->recv.max_uni_stream_id = > > + quic_stream_streams_to_id(p->max_streams_uni, type); > > + streams->recv.next_uni_stream_id = type; > > +} > > diff --git a/net/quic/stream.h b/net/quic/stream.h > > new file mode 100644 > > index 000000000000..c53d9358605c > > --- /dev/null > > +++ b/net/quic/stream.h > > @@ -0,0 +1,136 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* QUIC kernel implementation > > + * (C) Copyright Red Hat Corp. 2023 > > + * > > + * This file is part of the QUIC kernel implementation > > + * > > + * Written or modified by: > > + * Xin Long <[email protected]> > > + */ > > + > > +#define QUIC_DEF_STREAMS 100 > > +#define QUIC_MAX_STREAMS 4096ULL > > + > > +/* > > + * rfc9000#section-2.1: > > + * > > + * The least significant bit (0x01) of the stream ID identifies the initiator of the stream. > > + * Client-initiated streams have even-numbered stream IDs (with the bit set to 0), and > > + * server-initiated streams have odd-numbered stream IDs (with the bit set to 1). > > + * > > + * The second least significant bit (0x02) of the stream ID distinguishes between bidirectional > > + * streams (with the bit set to 0) and unidirectional streams (with the bit set to 1). > > + */ > > +#define QUIC_STREAM_TYPE_BITS 2 > > +#define QUIC_STREAM_ID_STEP BIT(QUIC_STREAM_TYPE_BITS) > > + > > +#define QUIC_STREAM_TYPE_CLIENT_BIDI 0x00 > > +#define QUIC_STREAM_TYPE_SERVER_BIDI 0x01 > > +#define QUIC_STREAM_TYPE_CLIENT_UNI 0x02 > > +#define QUIC_STREAM_TYPE_SERVER_UNI 0x03 > > + > > +struct quic_stream { > > + struct hlist_node node; > > + s64 id; /* Stream ID as defined in RFC 9000 Section 2.1 */ > > + struct { > > + /* Sending-side stream level flow control */ > > + u64 last_max_bytes; /* Maximum send offset advertised by peer at last update */ > > + u64 max_bytes; /* Current maximum offset we are allowed to send to */ > > + u64 bytes; /* Bytes already sent to peer */ > > + > > + u32 errcode; /* Application error code to send in RESET_STREAM */ > > + u32 frags; /* Number of sent STREAM frames not yet acknowledged */ > > + u8 state; /* Send stream state, per rfc9000#section-3.1 */ > > + > > + u8 data_blocked:1; /* True if flow control blocks sending more data */ > > + u8 done:1; /* True if application indicated end of stream (FIN sent) */ > > + } send; > > + struct { > > + /* Receiving-side stream level flow control */ > > + u64 max_bytes; /* Maximum offset peer is allowed to send to */ > > + u64 window; /* Remaining receive window before advertise a new limit */ > > + u64 bytes; /* Bytes consumed by application from the stream */ > > + > > + u64 highest; /* Highest received offset */ > > + u64 offset; /* Offset up to which data is in buffer or consumed */ > > + u64 finalsz; /* Final size of the stream if FIN received */ > > + > > + u32 frags; /* Number of received STREAM frames pending reassembly */ > > + u8 state; /* Receive stream state, per rfc9000#section-3.2 */ > > + > > + u8 stop_sent:1; /* True if STOP_SENDING has been sent */ > > + u8 done:1; /* True if FIN received and final size validated */ > > + } recv; > > +}; > > + > > +struct quic_stream_table { > > + struct quic_shash_table ht; /* Hash table storing all active streams */ > > + > > + struct { > > + /* Parameters received from peer, defined in rfc9000#section-18.2 */ > > + u64 max_stream_data_bidi_remote; /* initial_max_stream_data_bidi_remote */ > > + u64 max_stream_data_bidi_local; /* initial_max_stream_data_bidi_local */ > > + u64 max_stream_data_uni; /* initial_max_stream_data_uni */ > > + u64 max_streams_bidi; /* initial_max_streams_bidi */ > > + u64 max_streams_uni; /* initial_max_streams_uni */ > > + > > + s64 next_bidi_stream_id; /* Next bidi stream ID to be opened */ > > + s64 next_uni_stream_id; /* Next uni stream ID to be opened */ > > + s64 max_bidi_stream_id; /* Highest allowed bidi stream ID */ > > + s64 max_uni_stream_id; /* Highest allowed uni stream ID */ > > + s64 active_stream_id; /* Most recently opened stream ID */ > > + > > + u8 bidi_blocked:1; /* True if STREAMS_BLOCKED_BIDI was sent and not ACKed */ > > + u8 uni_blocked:1; /* True if STREAMS_BLOCKED_UNI was sent and not ACKed */ > > + u16 streams_bidi; /* Number of currently active bidi streams */ > > + u16 streams_uni; /* Number of currently active uni streams */ > > + } send; > > + struct { > > + /* Our advertised limits to the peer, per rfc9000#section-18.2 */ > > + u64 max_stream_data_bidi_remote; /* initial_max_stream_data_bidi_remote */ > > + u64 max_stream_data_bidi_local; /* initial_max_stream_data_bidi_local */ > > + u64 max_stream_data_uni; /* initial_max_stream_data_uni */ > > + u64 max_streams_bidi; /* initial_max_streams_bidi */ > > + u64 max_streams_uni; /* initial_max_streams_uni */ > > + > > + s64 next_bidi_stream_id; /* Next expected bidi stream ID from peer */ > > + s64 next_uni_stream_id; /* Next expected uni stream ID from peer */ > > + s64 max_bidi_stream_id; /* Current allowed bidi stream ID range */ > > + s64 max_uni_stream_id; /* Current allowed uni stream ID range */ > > + > > + u8 bidi_pending:1; /* True if MAX_STREAMS_BIDI needs to be sent */ > > + u8 uni_pending:1; /* True if MAX_STREAMS_UNI needs to be sent */ > > + u16 streams_bidi; /* Number of currently open bidi streams */ > > + u16 streams_uni; /* Number of currently open uni streams */ > > + } recv; > > +}; > > + > > +static inline u64 quic_stream_id_to_streams(s64 stream_id) > > +{ > > + return (u64)(stream_id >> QUIC_STREAM_TYPE_BITS) + 1; > > +} > > + > > +static inline s64 quic_stream_streams_to_id(u64 streams, u8 type) > > +{ > > + return (s64)((streams - 1) << QUIC_STREAM_TYPE_BITS) | type; > > +} > > + > > +struct quic_stream *quic_stream_send_get(struct quic_stream_table *streams, s64 stream_id, > > + u32 flags, bool is_serv); > > +struct quic_stream *quic_stream_recv_get(struct quic_stream_table *streams, s64 stream_id, > > + bool is_serv); > > +void quic_stream_send_put(struct quic_stream_table *streams, struct quic_stream *stream, > > + bool is_serv); > > +void quic_stream_recv_put(struct quic_stream_table *streams, struct quic_stream *stream, > > + bool is_serv); > > + > > +bool quic_stream_max_streams_update(struct quic_stream_table *streams, s64 *max_uni, s64 *max_bidi); > > +bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id, bool send); > > +struct quic_stream *quic_stream_find(struct quic_stream_table *streams, s64 stream_id); > > + > > +void quic_stream_get_param(struct quic_stream_table *streams, struct quic_transport_param *p, > > + bool is_serv); > > +void quic_stream_set_param(struct quic_stream_table *streams, struct quic_transport_param *p, > > + bool is_serv); > > +void quic_stream_free(struct quic_stream_table *streams); > > +int quic_stream_init(struct quic_stream_table *streams); >