[PATCH 2 of 4] Simplified sendfile(SF_NODISKIO) usage
Sergey Kandaurov
pluknet at nginx.com
Tue Nov 30 12:05:03 UTC 2021
On Thu, Nov 11, 2021 at 07:21:10AM +0300, Maxim Dounin wrote:
> # HG changeset patch
> # User Maxim Dounin <mdounin at mdounin.ru>
> # Date 1636603886 -10800
> # Thu Nov 11 07:11:26 2021 +0300
> # Node ID 4a954e89b1ae8539bbe08c5afc1d5c9828d82d6f
> # Parent 0fb75ef9dbca698e5e855145cf6a12180a36d400
> Simplified sendfile(SF_NODISKIO) usage.
>
> Starting with FreeBSD 11, there is no need to use AIO operations to preload
> data into cache for sendfile(SF_NODISKIO) to work. Instead, sendfile()
> handles non-blocking loading data from disk by itself. It still can, however,
> return EBUSY if a page is already being loaded (for example, by a different
> process). If this happens, we now post an event for the next event loop
> iteration, so sendfile() is retried "after a short period", as manpage
> recommends.
>
> The limit of the number of EBUSY tolerated without any progress is preserved,
> but now it does not result in an alert, since on an idle system event loop
> iteration might be very short and EBUSY can happen many times in a row.
> Instead, SF_NODISKIO is simply disabled for one call once the limit is
> reached.
>
> With this change, sendfile(SF_NODISKIO) is now used automatically as long as
> sendfile() is enabled, and no longer requires "aio on;".
>
> diff --git a/auto/os/freebsd b/auto/os/freebsd
> --- a/auto/os/freebsd
> +++ b/auto/os/freebsd
> @@ -44,12 +44,10 @@ if [ $osreldate -gt 300007 ]; then
> CORE_SRCS="$CORE_SRCS $FREEBSD_SENDFILE_SRCS"
> fi
>
> -if [ $NGX_FILE_AIO = YES ]; then
> - if [ $osreldate -gt 502103 ]; then
> - echo " + sendfile()'s SF_NODISKIO found"
> +if [ $osreldate -gt 1100000 ]; then
> + echo " + sendfile()'s SF_NODISKIO found"
>
> - have=NGX_HAVE_AIO_SENDFILE . auto/have
> - fi
> + have=NGX_HAVE_SENDFILE_NODISKIO . auto/have
> fi
>
> # POSIX semaphores
We could check the exact __FreeBSD_version number 1100093 that was
at the time the new sendfile() appeared, which is more accurate.
https://cgit.freebsd.org/src/commit/?id=2bab0c553588
https://cgit.freebsd.org/src/tree/sys/sys/param.h?id=2bab0c553588#n48
Unfortunately, it was not bumped (same as with SF_NODISKIO in 5.2.1).
> diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h
> --- a/src/core/ngx_buf.h
> +++ b/src/core/ngx_buf.h
> @@ -90,9 +90,6 @@ struct ngx_output_chain_ctx_s {
>
> #if (NGX_HAVE_FILE_AIO || NGX_COMPAT)
> ngx_output_chain_aio_pt aio_handler;
> -#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
> - ssize_t (*aio_preload)(ngx_buf_t *file);
> -#endif
> #endif
>
> #if (NGX_THREADS || NGX_COMPAT)
> diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
> --- a/src/core/ngx_connection.h
> +++ b/src/core/ngx_connection.h
> @@ -185,7 +185,7 @@ struct ngx_connection_s {
>
> unsigned need_last_buf:1;
>
> -#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
> +#if (NGX_HAVE_SENDFILE_NODISKIO || NGX_COMPAT)
> unsigned busy_count:2;
> #endif
>
> diff --git a/src/core/ngx_module.h b/src/core/ngx_module.h
> --- a/src/core/ngx_module.h
> +++ b/src/core/ngx_module.h
> @@ -41,7 +41,7 @@
> #define NGX_MODULE_SIGNATURE_3 "0"
> #endif
>
> -#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
> +#if (NGX_HAVE_SENDFILE_NODISKIO || NGX_COMPAT)
> #define NGX_MODULE_SIGNATURE_4 "1"
> #else
> #define NGX_MODULE_SIGNATURE_4 "0"
> diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c
> --- a/src/core/ngx_output_chain.c
> +++ b/src/core/ngx_output_chain.c
> @@ -29,10 +29,6 @@
>
> static ngx_inline ngx_int_t
> ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
> -#if (NGX_HAVE_AIO_SENDFILE)
> -static ngx_int_t ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx,
> - ngx_file_t *file);
> -#endif
> static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
> ngx_chain_t **chain, ngx_chain_t *in);
> static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
> @@ -283,12 +279,6 @@ ngx_output_chain_as_is(ngx_output_chain_
> buf->in_file = 0;
> }
>
> -#if (NGX_HAVE_AIO_SENDFILE)
> - if (ctx->aio_preload && buf->in_file) {
> - (void) ngx_output_chain_aio_setup(ctx, buf->file);
> - }
> -#endif
> -
> if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
> return 0;
> }
> @@ -301,28 +291,6 @@ ngx_output_chain_as_is(ngx_output_chain_
> }
>
>
> -#if (NGX_HAVE_AIO_SENDFILE)
> -
> -static ngx_int_t
> -ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
> -{
> - ngx_event_aio_t *aio;
> -
> - if (file->aio == NULL && ngx_file_aio_init(file, ctx->pool) != NGX_OK) {
> - return NGX_ERROR;
> - }
> -
> - aio = file->aio;
> -
> - aio->data = ctx->filter_ctx;
> - aio->preload_handler = ctx->aio_preload;
> -
> - return NGX_OK;
> -}
> -
> -#endif
> -
> -
> static ngx_int_t
> ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
> ngx_chain_t *in)
After this change, ngx_file_aio_init() doesn't need to be external.
It is only used in ngx_file_aio_read() in corresponding ngx_*_aio_read.c.
> diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h
> --- a/src/event/ngx_event.h
> +++ b/src/event/ngx_event.h
> @@ -147,10 +147,6 @@ struct ngx_event_aio_s {
>
> ngx_fd_t fd;
>
> -#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
> - ssize_t (*preload_handler)(ngx_buf_t *file);
> -#endif
> -
> #if (NGX_HAVE_EVENTFD)
> int64_t res;
> #endif
> 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
> @@ -19,10 +19,6 @@ typedef struct {
> static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,
> ngx_file_t *file);
> static void ngx_http_copy_aio_event_handler(ngx_event_t *ev);
> -#if (NGX_HAVE_AIO_SENDFILE)
> -static ssize_t ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file);
> -static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);
> -#endif
> #endif
> #if (NGX_THREADS)
> static ngx_int_t ngx_http_copy_thread_handler(ngx_thread_task_t *task,
> @@ -128,9 +124,6 @@ ngx_http_copy_filter(ngx_http_request_t
> #if (NGX_HAVE_FILE_AIO)
> if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {
> ctx->aio_handler = ngx_http_copy_aio_handler;
> -#if (NGX_HAVE_AIO_SENDFILE)
> - ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;
> -#endif
> }
> #endif
>
> @@ -207,53 +200,6 @@ ngx_http_copy_aio_event_handler(ngx_even
> ngx_http_run_posted_requests(c);
> }
>
> -
> -#if (NGX_HAVE_AIO_SENDFILE)
> -
> -static ssize_t
> -ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file)
> -{
> - ssize_t n;
> - static u_char buf[1];
> - ngx_event_aio_t *aio;
> - ngx_http_request_t *r;
> - ngx_output_chain_ctx_t *ctx;
> -
> - n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL);
> -
> - if (n == NGX_AGAIN) {
> - aio = file->file->aio;
> - aio->handler = ngx_http_copy_aio_sendfile_event_handler;
> -
> - r = aio->data;
> - r->main->blocked++;
> - r->aio = 1;
> -
> - ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
> - ctx->aio = 1;
> - }
> -
> - return n;
> -}
> -
> -
> -static void
> -ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
> -{
> - ngx_event_aio_t *aio;
> - ngx_http_request_t *r;
> -
> - aio = ev->data;
> - r = aio->data;
> -
> - r->main->blocked--;
> - r->aio = 0;
> - ev->complete = 0;
> -
> - r->connection->write->handler(r->connection->write);
> -}
> -
> -#endif
> #endif
>
>
> diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c
> --- a/src/os/unix/ngx_freebsd_sendfile_chain.c
> +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
> @@ -32,22 +32,21 @@
> ngx_chain_t *
> ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
> {
> - int rc, flags;
> - off_t send, prev_send, sent;
> - size_t file_size;
> - ssize_t n;
> - ngx_uint_t eintr, eagain;
> - ngx_err_t err;
> - ngx_buf_t *file;
> - ngx_event_t *wev;
> - ngx_chain_t *cl;
> - ngx_iovec_t header, trailer;
> - struct sf_hdtr hdtr;
> - struct iovec headers[NGX_IOVS_PREALLOCATE];
> - struct iovec trailers[NGX_IOVS_PREALLOCATE];
> -#if (NGX_HAVE_AIO_SENDFILE)
> - ngx_uint_t ebusy;
> - ngx_event_aio_t *aio;
> + int rc, flags;
> + off_t send, prev_send, sent;
> + size_t file_size;
> + ssize_t n;
> + ngx_uint_t eintr, eagain;
> + ngx_err_t err;
> + ngx_buf_t *file;
> + ngx_event_t *wev;
> + ngx_chain_t *cl;
> + ngx_iovec_t header, trailer;
> + struct sf_hdtr hdtr;
> + struct iovec headers[NGX_IOVS_PREALLOCATE];
> + struct iovec trailers[NGX_IOVS_PREALLOCATE];
> +#if (NGX_HAVE_SENDFILE_NODISKIO)
> + ngx_uint_t ebusy;
> #endif
After ngx_event_aio_t *aio variable removal,
this block could be placed under "eintr, eagain" line
(which by itself looks unsorted).
The remaining part looks good to me.
>
> wev = c->write;
> @@ -77,11 +76,6 @@ ngx_freebsd_sendfile_chain(ngx_connectio
> eagain = 0;
> flags = 0;
>
> -#if (NGX_HAVE_AIO_SENDFILE && NGX_SUPPRESS_WARN)
> - aio = NULL;
> - file = NULL;
> -#endif
> -
> header.iovs = headers;
> header.nalloc = NGX_IOVS_PREALLOCATE;
>
> @@ -90,7 +84,7 @@ ngx_freebsd_sendfile_chain(ngx_connectio
>
> for ( ;; ) {
> eintr = 0;
> -#if (NGX_HAVE_AIO_SENDFILE)
> +#if (NGX_HAVE_SENDFILE_NODISKIO)
> ebusy = 0;
> #endif
> prev_send = send;
> @@ -179,9 +173,8 @@ ngx_freebsd_sendfile_chain(ngx_connectio
>
> sent = 0;
>
> -#if (NGX_HAVE_AIO_SENDFILE)
> - aio = file->file->aio;
> - flags = (aio && aio->preload_handler) ? SF_NODISKIO : 0;
> +#if (NGX_HAVE_SENDFILE_NODISKIO)
> + flags = (c->busy_count <= 2) ? SF_NODISKIO : 0;
> #endif
>
> rc = sendfile(file->file->fd, c->fd, file->file_pos,
> @@ -199,7 +192,7 @@ ngx_freebsd_sendfile_chain(ngx_connectio
> eintr = 1;
> break;
>
> -#if (NGX_HAVE_AIO_SENDFILE)
> +#if (NGX_HAVE_SENDFILE_NODISKIO)
> case NGX_EBUSY:
> ebusy = 1;
> break;
> @@ -252,54 +245,30 @@ ngx_freebsd_sendfile_chain(ngx_connectio
>
> in = ngx_chain_update_sent(in, sent);
>
> -#if (NGX_HAVE_AIO_SENDFILE)
> +#if (NGX_HAVE_SENDFILE_NODISKIO)
>
> if (ebusy) {
> - if (aio->event.active) {
> - /*
> - * tolerate duplicate calls; they can happen due to subrequests
> - * or multiple calls of the next body filter from a filter
> - */
> -
> - if (sent) {
> - c->busy_count = 0;
> - }
> -
> - return in;
> - }
> -
> if (sent == 0) {
> c->busy_count++;
>
> - if (c->busy_count > 2) {
> - ngx_log_error(NGX_LOG_ALERT, c->log, 0,
> - "sendfile(%V) returned busy again",
> - &file->file->name);
> -
> - c->busy_count = 0;
> - aio->preload_handler = NULL;
> -
> - send = prev_send;
> - continue;
> - }
> + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
> + "sendfile() busy, count:%d", c->busy_count);
>
> } else {
> c->busy_count = 0;
> }
>
> - n = aio->preload_handler(file);
> -
> - if (n > 0) {
> - send = prev_send + sent;
> - continue;
> + if (wev->posted) {
> + ngx_delete_posted_event(wev);
> }
>
> + ngx_post_event(wev, &ngx_posted_next_events);
> +
> + wev->ready = 0;
> return in;
> }
>
> - if (flags == SF_NODISKIO) {
> - c->busy_count = 0;
> - }
> + c->busy_count = 0;
>
> #endif
>
>
More information about the nginx-devel
mailing list