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

Maxim Dounin mdounin at mdounin.ru
Thu Nov 11 03:10:21 UTC 2021


# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1636599377 -10800
#      Thu Nov 11 05:56:17 2021 +0300
# Node ID 76e072a6947a221868705c13973de15319c0d921
# Parent  82b750b20c5205d685e59031247fe898f011394e
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 processig
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 do not return EBUSY.

Fix is to post a write event on HTTP/2 connection in the thread event
handler (and aio preload handler).  This ensures that sendfile() will be
completed and stream processing will be resumed by HTTP/2 code.

diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -250,6 +250,21 @@ ngx_http_copy_aio_sendfile_event_handler
     r->aio = 0;
     ev->complete = 0;
 
+#if (NGX_HTTP_V2)
+
+    if (r->stream) {
+        /*
+         * for HTTP/2, trigger a write event on the main connection
+         * to handle sendfile() preload
+         */
+
+        ngx_post_event(r->stream->connection->connection->write,
+                       &ngx_posted_events);
+        return;
+    }
+
+#endif
+
     r->connection->write->handler(r->connection->write);
 }
 
@@ -323,6 +338,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, trigger a write event on the main connection
+         * to handle sendfile() in threads
+         */
+
+        ngx_post_event(r->stream->connection->connection->write,
+                       &ngx_posted_events);
+    }
+
+#endif
+
     if (r->done) {
         /*
          * trigger connection event handler if the subrequest was
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -3905,6 +3905,20 @@ ngx_http_upstream_thread_event_handler(n
     r->main->blocked--;
     r->aio = 0;
 
+#if (NGX_HTTP_V2)
+
+    if (r->stream) {
+        /*
+         * for HTTP/2, trigger a write event on the main connection
+         * to handle sendfile() in threads
+         */
+
+        ngx_post_event(r->stream->connection->connection->write,
+                       &ngx_posted_events);
+    }
+
+#endif
+
     if (r->done) {
         /*
          * trigger connection event handler if the subrequest was



More information about the nginx-devel mailing list