Sergey Kandaurov
November 18, 2021 11:48AM
> On 16 Nov 2021, at 17:41, Maxim Dounin <mdounin@mdounin.ru> wrote:
>
> Hello!
>
> On Tue, Nov 16, 2021 at 02:59:46PM +0300, Sergey Kandaurov wrote:
>
>>
>>> On 11 Nov 2021, at 06:10, Maxim Dounin <mdounin@mdounin.ru> wrote:
>>>
>>> # HG changeset patch
>>> # User Maxim Dounin <mdounin@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
>>
>> processing
>
> Fixed, thnx.
>
>>> 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.
>>
>> does not
>
> Fixed, thnx.
>
>>> 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
>>> +
>>
>> This thread event handler is used not only for sendfile() completion,
>> but also to complete reading in threads a buffered upstream response.
>> In this case, posting a write event on HTTP/2 connection looks
>> unnecessary, since there is no sendfile() in action, it will do nothing.
>> On the other hand, if it is indeed used to complete a sendfile() task,
>> which needs to invoke http2 write handler, calling write_event_handler()
>> directly from thread event handler seems to be redundant: it could be
>> optimized away since http2 write handler will normally end up in posting
>> a write event on the main connection, anyway, see the call sequence
>> ngx_http_v2_write_handler() -> ngx_http_v2_send_output_queue()
>> -> ngx_http_v2_data_frame_handler() -> ngx_http_v2_handle_stream().
>
> [...]
>
>> So, it could be narrowed down, something like the aio preload handler:
>>
>> diff -r 76e072a6947a -r 5f48b9a797d1 src/http/ngx_http_copy_filter_module.c
>> --- a/src/http/ngx_http_copy_filter_module.c Thu Nov 11 05:56:17 2021 +0300
>> +++ b/src/http/ngx_http_copy_filter_module.c Mon Nov 15 21:04:26 2021 +0000
>> @@ -340,7 +340,7 @@
>>
>> #if (NGX_HTTP_V2)
>>
>> - if (r->stream) {
>> + if (r->stream && r->stream->connection->connection->sendfile_task) {
>> /*
>> * for HTTP/2, trigger a write event on the main connection
>> * to handle sendfile() in threads
>> @@ -348,6 +348,7 @@
>>
>> ngx_post_event(r->stream->connection->connection->write,
>> &ngx_posted_events);
>> + return;
>> }
>>
>> #endif
>
> This "return" won't work, since even with sendfile() enabled and
> being used, the handler can be called for non-sendfile operations
> as well.
>
> That is, both posting an event to the main connection _and_
> calling request write handler are required. This might be
> redundant in some cases, but there is no reasonable way to avoid
> this with sendfile() enabled.
>
> Checking sendfile_task might be used to avoid extra posted event
> with sendfile disabled, but it looks overcomplicated to me and I
> don't think it worth the effort. It's at most a minor
> optimization.
>

Fair enough.

>>> 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
>>>
>>
>> I could not figure out, how this part is related, since upstream
>> thread handler is only enabled with "aio_write on;" to write down
>> a buffered upstream response to disk. It doesn't seem to be used
>> with sendfile().
>
> Thread handlers are set on per-file basis. As a result, if
> aio_write is enabled, the ngx_http_upstream_thread_event_handler()
> handler can be used for sendfile() as well.

Indeed, it is the case. I was able to reproduce it with a variant
of slice.t, updated to "listen .. http2". The patch fixes it.

>
> Also note the following "trigger connection event handler..."
> part: it is also only needed for sendfile(), yet present in the
> ngx_http_upstream_thread_event_handler().

Yep, I've triggered the original problem fixed with the existing condition,
though I had to back out sendfile_max_chunk rework with posted next events
to catch it, as otherwise the write event was observed to be always posted.

--
Sergey Kandaurov

_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

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

Maxim Dounin 467 November 10, 2021 10:12PM

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

Sergey Kandaurov 102 November 16, 2021 07:02AM

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

Maxim Dounin 106 November 16, 2021 09:42AM

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

Sergey Kandaurov 254 November 18, 2021 11:48AM

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

Maxim Dounin 95 November 22, 2021 10:14PM

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

Sergey Kandaurov 143 November 23, 2021 02:46AM

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

Maxim Dounin 100 November 23, 2021 10:52PM

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

Sergey Kandaurov 217 November 24, 2021 10:52AM

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

Maxim Dounin 208 November 24, 2021 02:08PM

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

Sergey Kandaurov 261 November 25, 2021 08:16AM

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

Maxim Dounin 157 November 25, 2021 08:56AM

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

Sergey Kandaurov 122 November 25, 2021 12:44PM

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

Maxim Dounin 260 November 25, 2021 02:08PM



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

Online Users

Guests: 306
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready