Mercurial > hg > nginx
annotate src/event/quic/ngx_event_quic_socket.c @ 8946:56dec0d4e5b1 quic
QUIC: avoid excessive buffer allocations in stream output.
Previously, when a few bytes were send to a QUIC stream by the application, a
4K buffer was allocated for these bytes. Then a STREAM frame was created and
that entire buffer was used as data for that frame. The frame with the buffer
were in use up until the frame was acked by client. Meanwhile, when more
bytes were send to the stream, more buffers were allocated and assigned as
data to newer STREAM frames. In this scenario most buffer memory is unused.
Now the unused part of the stream output buffer is available for further
stream output while earlier parts of the buffer are waiting to be acked.
This is achieved by splitting the output buffer.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Fri, 24 Dec 2021 18:13:51 +0300 |
parents | fb41e37ddeb0 |
children | 32daba3aabb2 |
rev | line source |
---|---|
8763 | 1 |
2 /* | |
3 * Copyright (C) Nginx, Inc. | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_event.h> | |
10 #include <ngx_event_quic_connection.h> | |
11 | |
12 | |
13 static ngx_int_t ngx_quic_create_temp_socket(ngx_connection_t *c, | |
14 ngx_quic_connection_t *qc, ngx_str_t *dcid, ngx_quic_path_t *path, | |
15 ngx_quic_client_id_t *cid); | |
16 | |
17 | |
18 ngx_int_t | |
19 ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
20 ngx_quic_header_t *pkt) | |
21 { | |
22 ngx_quic_path_t *path; | |
23 ngx_quic_socket_t *qsock; | |
24 ngx_quic_client_id_t *cid; | |
25 | |
26 /* | |
27 * qc->nclient_ids = 0 | |
28 * qc->nsockets = 0 | |
29 * qc->max_retired_seqnum = 0 | |
30 * qc->client_seqnum = 0 | |
31 */ | |
32 | |
33 ngx_queue_init(&qc->sockets); | |
34 ngx_queue_init(&qc->free_sockets); | |
35 | |
36 ngx_queue_init(&qc->paths); | |
37 ngx_queue_init(&qc->free_paths); | |
38 | |
39 ngx_queue_init(&qc->client_ids); | |
40 ngx_queue_init(&qc->free_client_ids); | |
41 | |
42 qc->tp.original_dcid.len = pkt->odcid.len; | |
43 qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid); | |
44 if (qc->tp.original_dcid.data == NULL) { | |
45 return NGX_ERROR; | |
46 } | |
47 | |
48 /* socket to use for further processing */ | |
49 qsock = ngx_quic_alloc_socket(c, qc); | |
50 if (qsock == NULL) { | |
51 return NGX_ERROR; | |
52 } | |
53 | |
54 /* socket is listening at new server id (autogenerated) */ | |
55 if (ngx_quic_listen(c, qc, qsock) != NGX_OK) { | |
56 return NGX_ERROR; | |
57 } | |
58 | |
59 qc->tp.initial_scid.len = qsock->sid.len; | |
60 qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len); | |
61 if (qc->tp.initial_scid.data == NULL) { | |
62 goto failed; | |
63 } | |
64 ngx_memcpy(qc->tp.initial_scid.data, qsock->sid.id, qsock->sid.len); | |
65 | |
66 /* for all packets except first, this is set at udp layer */ | |
67 c->udp = &qsock->udp; | |
68 | |
69 /* ngx_quic_get_connection(c) macro is now usable */ | |
70 | |
71 /* we have a client identified by scid */ | |
72 cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL); | |
73 if (cid == NULL) { | |
74 goto failed; | |
75 } | |
76 | |
77 /* the client arrived from this path */ | |
78 path = ngx_quic_add_path(c, c->sockaddr, c->socklen); | |
79 if (path == NULL) { | |
80 goto failed; | |
81 } | |
82 | |
83 if (pkt->validated) { | |
84 path->state = NGX_QUIC_PATH_VALIDATED; | |
8940
fb41e37ddeb0
QUIC: decoupled path state and limitation status.
Vladimir Homutov <vl@nginx.com>
parents:
8939
diff
changeset
|
85 path->limited = 0; |
8763 | 86 } |
87 | |
88 /* now bind socket to client and path */ | |
89 ngx_quic_connect(c, qsock, path, cid); | |
90 | |
91 if (ngx_quic_create_temp_socket(c, qc, &pkt->odcid, path, cid) != NGX_OK) { | |
92 goto failed; | |
93 } | |
94 | |
95 /* use this socket as default destination */ | |
96 qc->socket = qsock; | |
97 | |
98 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
99 "quic active socket is #%uL:%uL:%uL (%s)", | |
100 qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum, | |
101 ngx_quic_path_state_str(qsock->path)); | |
102 | |
103 return NGX_OK; | |
104 | |
105 failed: | |
106 | |
107 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); | |
108 c->udp = NULL; | |
109 | |
110 return NGX_ERROR; | |
111 } | |
112 | |
113 | |
114 static ngx_int_t | |
115 ngx_quic_create_temp_socket(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
116 ngx_str_t *dcid, ngx_quic_path_t *path, ngx_quic_client_id_t *cid) | |
117 { | |
118 ngx_str_t id; | |
119 ngx_quic_socket_t *qsock; | |
120 ngx_quic_server_id_t *sid; | |
121 | |
122 qsock = ngx_quic_alloc_socket(c, qc); | |
123 if (qsock == NULL) { | |
124 return NGX_ERROR; | |
125 } | |
126 | |
127 sid = &qsock->sid; | |
128 | |
129 sid->seqnum = NGX_QUIC_UNSET_PN; /* mark socket as temporary */ | |
130 | |
131 sid->len = dcid->len; | |
132 ngx_memcpy(sid->id, dcid->data, dcid->len); | |
133 | |
134 id.len = sid->len; | |
135 id.data = sid->id; | |
136 | |
137 ngx_insert_udp_connection(c, &qsock->udp, &id); | |
138 | |
139 ngx_queue_insert_tail(&qc->sockets, &qsock->queue); | |
140 | |
141 qc->nsockets++; | |
142 qsock->quic = qc; | |
143 | |
144 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
145 "quic socket #%L listening at sid:%xV nsock:%ui", | |
146 (int64_t) sid->seqnum, &id, qc->nsockets); | |
147 | |
148 ngx_quic_connect(c, qsock, path, cid); | |
149 | |
150 return NGX_OK; | |
151 } | |
152 | |
153 | |
154 ngx_quic_socket_t * | |
155 ngx_quic_alloc_socket(ngx_connection_t *c, ngx_quic_connection_t *qc) | |
156 { | |
157 ngx_queue_t *q; | |
158 ngx_quic_socket_t *sock; | |
159 | |
160 if (!ngx_queue_empty(&qc->free_sockets)) { | |
161 | |
162 q = ngx_queue_head(&qc->free_sockets); | |
163 sock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
164 | |
165 ngx_queue_remove(&sock->queue); | |
166 | |
167 ngx_memzero(sock, sizeof(ngx_quic_socket_t)); | |
168 | |
169 } else { | |
170 | |
171 sock = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t)); | |
172 if (sock == NULL) { | |
173 return NULL; | |
174 } | |
175 } | |
176 | |
177 return sock; | |
178 } | |
179 | |
180 | |
181 void | |
182 ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock) | |
183 { | |
184 ngx_quic_connection_t *qc; | |
185 | |
186 qc = ngx_quic_get_connection(c); | |
187 | |
188 ngx_queue_remove(&qsock->queue); | |
189 ngx_queue_insert_head(&qc->free_sockets, &qsock->queue); | |
190 | |
191 ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node); | |
192 qc->nsockets--; | |
193 | |
194 if (qsock->path) { | |
195 ngx_quic_unref_path(c, qsock->path); | |
196 } | |
197 | |
198 if (qsock->cid) { | |
199 ngx_quic_unref_client_id(c, qsock->cid); | |
200 } | |
201 | |
202 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
203 "quic socket #%L closed nsock:%ui", | |
204 (int64_t) qsock->sid.seqnum, qc->nsockets); | |
205 } | |
206 | |
207 | |
8911
b09f055daa4e
QUIC: fixed handling of RETIRE_CONNECTION_ID frame.
Vladimir Homutov <vl@nginx.com>
parents:
8888
diff
changeset
|
208 void |
8763 | 209 ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path) |
210 { | |
211 ngx_quic_connection_t *qc; | |
212 | |
213 path->refcnt--; | |
214 | |
215 if (path->refcnt) { | |
216 return; | |
217 } | |
218 | |
219 qc = ngx_quic_get_connection(c); | |
220 | |
221 ngx_queue_remove(&path->queue); | |
222 ngx_queue_insert_head(&qc->free_paths, &path->queue); | |
223 | |
224 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
225 "quic path #%uL addr:%V removed", | |
226 path->seqnum, &path->addr_text); | |
227 } | |
228 | |
229 | |
230 ngx_int_t | |
231 ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
232 ngx_quic_socket_t *qsock) | |
233 { | |
234 ngx_str_t id; | |
235 ngx_quic_server_id_t *sid; | |
236 | |
237 sid = &qsock->sid; | |
238 | |
239 sid->len = NGX_QUIC_SERVER_CID_LEN; | |
240 | |
241 if (ngx_quic_create_server_id(c, sid->id) != NGX_OK) { | |
242 return NGX_ERROR; | |
243 } | |
244 | |
245 sid->seqnum = qc->server_seqnum++; | |
246 | |
247 id.data = sid->id; | |
248 id.len = sid->len; | |
249 | |
250 ngx_insert_udp_connection(c, &qsock->udp, &id); | |
251 | |
252 ngx_queue_insert_tail(&qc->sockets, &qsock->queue); | |
253 | |
254 qc->nsockets++; | |
255 qsock->quic = qc; | |
256 | |
257 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
258 "quic socket #%uL listening at sid:%xV nsock:%ui", | |
259 sid->seqnum, &id, qc->nsockets); | |
260 | |
261 return NGX_OK; | |
262 } | |
263 | |
264 | |
265 void | |
266 ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *sock, | |
267 ngx_quic_path_t *path, ngx_quic_client_id_t *cid) | |
268 { | |
269 sock->path = path; | |
270 path->refcnt++; | |
271 | |
272 sock->cid = cid; | |
273 cid->refcnt++; | |
274 | |
275 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
276 "quic socket #%L connected to cid #%uL path:%uL", | |
277 (int64_t) sock->sid.seqnum, | |
278 sock->cid->seqnum, path->seqnum); | |
279 } | |
280 | |
281 | |
282 void | |
283 ngx_quic_close_sockets(ngx_connection_t *c) | |
284 { | |
285 ngx_queue_t *q; | |
286 ngx_quic_socket_t *qsock; | |
287 ngx_quic_connection_t *qc; | |
288 | |
289 qc = ngx_quic_get_connection(c); | |
290 | |
291 while (!ngx_queue_empty(&qc->sockets)) { | |
292 q = ngx_queue_head(&qc->sockets); | |
293 qsock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
294 | |
295 ngx_quic_close_socket(c, qsock); | |
296 } | |
297 } | |
298 | |
299 | |
300 ngx_quic_socket_t * | |
301 ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum) | |
302 { | |
303 ngx_queue_t *q; | |
304 ngx_quic_socket_t *qsock; | |
305 ngx_quic_connection_t *qc; | |
306 | |
307 qc = ngx_quic_get_connection(c); | |
308 | |
309 for (q = ngx_queue_head(&qc->sockets); | |
310 q != ngx_queue_sentinel(&qc->sockets); | |
311 q = ngx_queue_next(q)) | |
312 { | |
313 qsock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
314 | |
315 if (qsock->sid.seqnum == seqnum) { | |
316 return qsock; | |
317 } | |
318 } | |
319 | |
320 return NULL; | |
321 } | |
322 | |
323 | |
324 ngx_quic_socket_t * | |
325 ngx_quic_get_unconnected_socket(ngx_connection_t *c) | |
326 { | |
327 ngx_queue_t *q; | |
328 ngx_quic_socket_t *sock; | |
329 ngx_quic_connection_t *qc; | |
330 | |
331 qc = ngx_quic_get_connection(c); | |
332 | |
333 for (q = ngx_queue_head(&qc->sockets); | |
334 q != ngx_queue_sentinel(&qc->sockets); | |
335 q = ngx_queue_next(q)) | |
336 { | |
337 sock = ngx_queue_data(q, ngx_quic_socket_t, queue); | |
338 | |
339 if (sock->cid == NULL) { | |
340 return sock; | |
341 } | |
342 } | |
343 | |
344 return NULL; | |
8888 | 345 } |