[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