Welcome! Log In Create A New Profile

Advanced

[nginx] HTTP/2: fixed sendfile() aio handling.

Maxim Dounin
November 25, 2021 02:06PM
details: https://hg.nginx.org/nginx/rev/a7a77549265e
branches:
changeset: 7975:a7a77549265e
user: Maxim Dounin <mdounin@mdounin.ru>
date: Thu Nov 25 22:02:10 2021 +0300
description:
HTTP/2: fixed sendfile() aio handling.

With sendfile() in threads ("aio threads; sendfile on;"), client connection
can block on writing, waiting for sendfile() to complete. In HTTP/2 this
might result in the request hang, since an attempt to continue processing
in thread event handler will call request's write event handler, which
is usually stopped by ngx_http_v2_send_chain(): it does nothing if there
are no additional data and stream->queued is set. Further, HTTP/2 resets
stream's c->write->ready to 0 if writing blocks, so just fixing
ngx_http_v2_send_chain() is not enough.

Can be reproduced with test suite on Linux with:

TEST_NGINX_GLOBALS_HTTP="aio threads; sendfile on;" prove h2*.t

The following tests currently fail: h2_keepalive.t, h2_priority.t,
h2_proxy_max_temp_file_size.t, h2.t, h2_trailers.t.

Similarly, sendfile() with AIO preloading on FreeBSD can block as well,
with similar results. This is, however, harder to reproduce, especially
on modern FreeBSD systems, since sendfile() usually does not return EBUSY.

Fix is to modify ngx_http_v2_send_chain() so it actually tries to send
data to the main connection when called, and to make sure that
c->write->ready is set by the relevant event handlers.

diffstat:

src/http/ngx_http_copy_filter_module.c | 32 +++++++++++++++++++++++++++++++-
src/http/ngx_http_upstream.c | 14 ++++++++++++++
src/http/v2/ngx_http_v2_filter_module.c | 29 ++++++++++++++++++++---------
3 files changed, 65 insertions(+), 10 deletions(-)

diffs (142 lines):

diff -r 555533169506 -r a7a77549265e src/http/ngx_http_copy_filter_module.c
--- a/src/http/ngx_http_copy_filter_module.c Thu Nov 25 22:02:05 2021 +0300
+++ b/src/http/ngx_http_copy_filter_module.c Thu Nov 25 22:02:10 2021 +0300
@@ -253,16 +253,32 @@ static void
ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
{
ngx_event_aio_t *aio;
+ ngx_connection_t *c;
ngx_http_request_t *r;

aio = ev->data;
r = aio->data;
+ c = r->connection;

r->main->blocked--;
r->aio = 0;
ev->complete = 0;

- r->connection->write->handler(r->connection->write);
+#if (NGX_HTTP_V2)
+
+ if (r->stream) {
+ /*
+ * for HTTP/2, update write event to make sure processing will
+ * reach the main connection to handle sendfile() preload
+ */
+
+ c->write->ready = 1;
+ c->write->active = 0;
+ }
+
+#endif
+
+ c->write->handler(c->write);
}

#endif
@@ -357,6 +373,20 @@ ngx_http_copy_thread_event_handler(ngx_e
r->main->blocked--;
r->aio = 0;

+#if (NGX_HTTP_V2)
+
+ if (r->stream) {
+ /*
+ * for HTTP/2, update write event to make sure processing will
+ * reach the main connection to handle sendfile() in threads
+ */
+
+ c->write->ready = 1;
+ c->write->active = 0;
+ }
+
+#endif
+
if (r->done) {
/*
* trigger connection event handler if the subrequest was
diff -r 555533169506 -r a7a77549265e src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c Thu Nov 25 22:02:05 2021 +0300
+++ b/src/http/ngx_http_upstream.c Thu Nov 25 22:02:10 2021 +0300
@@ -3927,6 +3927,20 @@ ngx_http_upstream_thread_event_handler(n
r->main->blocked--;
r->aio = 0;

+#if (NGX_HTTP_V2)
+
+ if (r->stream) {
+ /*
+ * for HTTP/2, update write event to make sure processing will
+ * reach the main connection to handle sendfile() in threads
+ */
+
+ c->write->ready = 1;
+ c->write->active = 0;
+ }
+
+#endif
+
if (r->done) {
/*
* trigger connection event handler if the subrequest was
diff -r 555533169506 -r a7a77549265e src/http/v2/ngx_http_v2_filter_module.c
--- a/src/http/v2/ngx_http_v2_filter_module.c Thu Nov 25 22:02:05 2021 +0300
+++ b/src/http/v2/ngx_http_v2_filter_module.c Thu Nov 25 22:02:10 2021 +0300
@@ -1432,6 +1432,9 @@ ngx_http_v2_send_chain(ngx_connection_t
size = 0;
#endif

+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 send chain: %p", in);
+
while (in) {
size = ngx_buf_size(in->buf);

@@ -1450,12 +1453,8 @@ ngx_http_v2_send_chain(ngx_connection_t
return NGX_CHAIN_ERROR;
}

- if (stream->queued) {
- fc->write->active = 1;
- fc->write->ready = 0;
-
- } else {
- fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
+ if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
}

return NULL;
@@ -1464,9 +1463,16 @@ ngx_http_v2_send_chain(ngx_connection_t
h2c = stream->connection;

if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
- fc->write->active = 1;
- fc->write->ready = 0;
- return in;
+
+ if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
+ fc->write->active = 1;
+ fc->write->ready = 0;
+ return in;
+ }
}

if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
@@ -1809,6 +1815,11 @@ ngx_http_v2_waiting_queue(ngx_http_v2_co
static ngx_inline ngx_int_t
ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
{
+ if (stream->queued == 0) {
+ fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
+ return NGX_OK;
+ }
+
stream->blocked = 1;

if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[nginx] HTTP/2: fixed sendfile() aio handling.

Maxim Dounin 26 November 25, 2021 02:06PM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 77
Record Number of Users: 6 on February 13, 2018
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready