# HG changeset patch # User Vladimir Homutov # Date 1627482198 -10800 # Node ID ad046179eb914f4a92e01c3fa6c35b830cadbb34 # Parent d80365ca678d9d09aaec94341702903e879cdeb3 QUIC: handle EAGAIN properly on UDP sockets. Previously, the error was ignored leading to unnecessary retransmits. Now, unsent frames are returned into output queue, state is reset, and timer is started for the next send attempt. diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -255,6 +255,7 @@ ngx_quic_new_connection(ngx_connection_t for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { ngx_queue_init(&qc->send_ctx[i].frames); + ngx_queue_init(&qc->send_ctx[i].sending); ngx_queue_init(&qc->send_ctx[i].sent); qc->send_ctx[i].largest_pn = NGX_QUIC_UNSET_PN; qc->send_ctx[i].largest_ack = NGX_QUIC_UNSET_PN; diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h --- a/src/event/quic/ngx_event_quic_connection.h +++ b/src/event/quic/ngx_event_quic_connection.h @@ -161,8 +161,9 @@ struct ngx_quic_send_ctx_s { uint64_t largest_ack; /* received from peer */ uint64_t largest_pn; /* received from peer */ - ngx_queue_t frames; - ngx_queue_t sent; + ngx_queue_t frames; /* generated frames */ + ngx_queue_t sending; /* frames assigned to pkt */ + ngx_queue_t sent; /* frames waiting ACK */ uint64_t pending_ack; /* non sent ack-eliciting */ uint64_t largest_range; diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -55,7 +55,7 @@ ngx_quic_handle_path_challenge_frame(ngx pad = ngx_min(1200, max); sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen); - if (sent == -1) { + if (sent < 0) { return NGX_ERROR; } @@ -606,7 +606,7 @@ ngx_quic_send_path_challenge(ngx_connect pad = ngx_min(1200, max); sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen); - if (sent == -1) { + if (sent < 0) { return NGX_ERROR; } @@ -618,7 +618,7 @@ ngx_quic_send_path_challenge(ngx_connect pad = ngx_min(1200, max); sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen); - if (sent == -1) { + if (sent < 0) { return NGX_ERROR; } diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c --- a/src/event/quic/ngx_event_quic_output.c +++ b/src/event/quic/ngx_event_quic_output.c @@ -39,11 +39,16 @@ #define NGX_QUIC_CC_MIN_INTERVAL 1000 /* 1s */ +#define NGX_QUIC_SOCKET_RETRY_DELAY 10 /* ms, for NGX_AGAIN on write */ + static ngx_int_t ngx_quic_socket_output(ngx_connection_t *c, ngx_quic_socket_t *qsock); static ngx_int_t ngx_quic_create_datagrams(ngx_connection_t *c, ngx_quic_socket_t *qsock); +static void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx); +static void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, + uint64_t pnum); #if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL)) static ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c, ngx_quic_socket_t *qsock); @@ -138,6 +143,7 @@ ngx_quic_create_datagrams(ngx_connection size_t len, min; ssize_t n; u_char *p; + uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST]; ngx_uint_t i, pad; ngx_quic_path_t *path; ngx_quic_send_ctx_t *ctx; @@ -145,7 +151,6 @@ ngx_quic_create_datagrams(ngx_connection static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; qc = ngx_quic_get_connection(c); - path = qsock->path; for ( ;; ) { @@ -167,6 +172,8 @@ ngx_quic_create_datagrams(ngx_connection ctx = &qc->send_ctx[i]; + preserved_pnum[i] = ctx->pnum; + if (ngx_quic_generate_ack(c, ctx) != NGX_OK) { return NGX_ERROR; } @@ -194,6 +201,19 @@ ngx_quic_create_datagrams(ngx_connection return NGX_ERROR; } + if (n == NGX_AGAIN) { + for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { + ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i]); + } + + ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY); + break; + } + + for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { + ngx_quic_commit_send(c, &qc->send_ctx[i]); + } + path->sent += len; } @@ -201,6 +221,57 @@ ngx_quic_create_datagrams(ngx_connection } +static void +ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx) +{ + ngx_queue_t *q; + ngx_quic_frame_t *f; + ngx_quic_congestion_t *cg; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + + cg = &qc->congestion; + + while (!ngx_queue_empty(&ctx->sending)) { + + q = ngx_queue_head(&ctx->sending); + f = ngx_queue_data(q, ngx_quic_frame_t, queue); + + ngx_queue_remove(q); + + if (f->pkt_need_ack && !qc->closing) { + ngx_queue_insert_tail(&ctx->sent, q); + + cg->in_flight += f->plen; + + } else { + ngx_quic_free_frame(c, f); + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic congestion send if:%uz", cg->in_flight); +} + + +static void +ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, + uint64_t pnum) +{ + ngx_queue_t *q; + + while (!ngx_queue_empty(&ctx->sending)) { + + q = ngx_queue_last(&ctx->sending); + ngx_queue_remove(q); + ngx_queue_insert_head(&ctx->frames, q); + } + + ctx->pnum = pnum; +} + + #if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL)) static ngx_uint_t @@ -264,9 +335,10 @@ ngx_quic_create_segments(ngx_connection_ size_t len, segsize; ssize_t n; u_char *p, *end; + uint64_t preserved_pnum; ngx_uint_t nseg; + ngx_quic_path_t *path; ngx_quic_send_ctx_t *ctx; - ngx_quic_path_t *path; ngx_quic_connection_t *qc; static u_char dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF]; @@ -286,6 +358,8 @@ ngx_quic_create_segments(ngx_connection_ nseg = 0; + preserved_pnum = ctx->pnum; + for ( ;; ) { len = ngx_min(segsize, (size_t) (end - p)); @@ -315,10 +389,20 @@ ngx_quic_create_segments(ngx_connection_ return NGX_ERROR; } + if (n == NGX_AGAIN) { + ngx_quic_revert_send(c, ctx, preserved_pnum); + + ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY); + break; + } + + ngx_quic_commit_send(c, ctx); + path->sent += n; p = dst; nseg = 0; + preserved_pnum = ctx->pnum; } } @@ -380,8 +464,8 @@ ngx_quic_send_segments(ngx_connection_t msg.msg_controllen = clen; n = ngx_sendmsg(c, &msg, 0); - if (n == -1) { - return NGX_ERROR; + if (n < 0) { + return n; } c->sent += n; @@ -622,32 +706,20 @@ ngx_quic_output_packet(ngx_connection_t ctx->pnum++; if (pkt.need_ack) { - /* move frames into the sent queue to wait for ack */ - - if (!qc->closing) { - q = ngx_queue_head(&ctx->frames); - f = ngx_queue_data(q, ngx_quic_frame_t, queue); - f->plen = res.len; + q = ngx_queue_head(&ctx->frames); + f = ngx_queue_data(q, ngx_quic_frame_t, queue); - do { - q = ngx_queue_head(&ctx->frames); - ngx_queue_remove(q); - ngx_queue_insert_tail(&ctx->sent, q); - } while (--nframes); - } - - cg->in_flight += res.len; - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic congestion send if:%uz", cg->in_flight); + f->plen = res.len; } while (nframes--) { q = ngx_queue_head(&ctx->frames); f = ngx_queue_data(q, ngx_quic_frame_t, queue); + f->pkt_need_ack = pkt.need_ack; + ngx_queue_remove(q); - ngx_quic_free_frame(c, f); + ngx_queue_insert_tail(&ctx->sending, q); } return res.len; @@ -658,37 +730,46 @@ static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len, struct sockaddr *sockaddr, socklen_t socklen) { - ngx_buf_t b; - socklen_t orig_socklen; - ngx_chain_t cl, *res; - struct sockaddr *orig_sockaddr; + ssize_t n; + struct iovec iov; + struct msghdr msg; +#if defined(NGX_HAVE_ADDRINFO_CMSG) + struct cmsghdr *cmsg; + char msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))]; +#endif - ngx_memzero(&b, sizeof(ngx_buf_t)); + ngx_memzero(&msg, sizeof(struct msghdr)); - b.pos = b.start = buf; - b.last = b.end = buf + len; - b.last_buf = 1; - b.temporary = 1; + iov.iov_len = len; + iov.iov_base = buf; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - cl.buf = &b; - cl.next= NULL; + msg.msg_name = sockaddr; + msg.msg_namelen = socklen; - orig_socklen = c->socklen; - orig_sockaddr = c->sockaddr; +#if defined(NGX_HAVE_ADDRINFO_CMSG) + if (c->listening && c->listening->wildcard && c->local_sockaddr) { + + msg.msg_control = msg_control; + msg.msg_controllen = sizeof(msg_control); + ngx_memzero(msg_control, sizeof(msg_control)); - c->sockaddr = sockaddr; - c->socklen = socklen; - - res = c->send_chain(c, &cl, 0); + cmsg = CMSG_FIRSTHDR(&msg); - c->sockaddr = orig_sockaddr; - c->socklen = orig_socklen; + msg.msg_controllen = ngx_set_srcaddr_cmsg(cmsg, c->local_sockaddr); + } +#endif - if (res == NGX_CHAIN_ERROR) { - return NGX_ERROR; + n = ngx_sendmsg(c, &msg, 0); + if (n < 0) { + return n; } - return len; + c->sent += n; + + return n; } @@ -945,9 +1026,7 @@ ngx_quic_send_early_cc(ngx_connection_t return NGX_ERROR; } - if (ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen) - == NGX_ERROR) - { + if (ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen) < 0) { return NGX_ERROR; } @@ -1006,7 +1085,7 @@ ngx_quic_send_retry(ngx_connection_t *c, #endif len = ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen); - if (len == NGX_ERROR) { + if (len < 0) { return NGX_ERROR; } @@ -1221,7 +1300,5 @@ ngx_quic_frame_sendto(ngx_connection_t * ctx->pnum++; - len = ngx_quic_send(c, res.data, res.len, sockaddr, socklen); - - return len; + return ngx_quic_send(c, res.data, res.len, sockaddr, socklen); } diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h --- a/src/event/quic/ngx_event_quic_transport.h +++ b/src/event/quic/ngx_event_quic_transport.h @@ -273,6 +273,7 @@ struct ngx_quic_frame_s { ngx_msec_t last; ssize_t len; unsigned need_ack:1; + unsigned pkt_need_ack:1; unsigned flush:1; ngx_chain_t *data;