[ PATCH ] Add preadv2 support with RWF_NOWAIT flag

Vadim Fedorenko vadimjunk at gmail.com
Tue Jan 9 15:06:00 UTC 2018


Introduction of thread pools is really good thing, but it adds
overhead to reading files which are already in page cache in linux.
With preadv2 (introduced in Linux 4.6) and RWF_NOWAIT flag (introduced
in Linux 4.14) we can eliminate this overhead. Needs glibc >= 2.26

# HG changeset patch
# User Vadim Fedorenko <vfedorenko at yandex-team.ru>
# Date 1515498853 -10800
#      Tue Jan 09 14:54:13 2018 +0300
# Node ID f955f9cddd38ce35e19c50b871558ca8739a1d4b
# Parent  6d2e92acb013224e6ef2c71c9e61ab07f0b03271
Add preadv2() with RWF_NOWAIT flag

Eliminate overhead with threads synchronization when cache file or
chain is in page cache already

diff -r 6d2e92acb013 -r f955f9cddd38 auto/unix
--- a/auto/unix Thu Dec 28 12:01:05 2017 +0200
+++ b/auto/unix Tue Jan 09 14:54:13 2018 +0300
@@ -726,6 +726,21 @@
                   if (n == -1) return 1"
 . auto/feature

+# preadv2() was introduced in Linux 4.6, glibc 2.26
+# RWF_NOWAIT flag was introduced in Linux 4.14
+
+ngx_feature="preadv2()"
+ngx_feature_name="NGX_HAVE_PREADV2_NONBLOCK"
+ngx_feature_run=no
+ngx_feature_incs='#include <sys/uio.h>'
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="char buf[1]; struct iovec vec[1]; ssize_t n;
+                  vec[0].iov_base = buf;
+                  vec[0].iov_len = 1;
+                  n = preadv2(0, vec, 1, 0, RWF_NOWAIT);
+                  if (n == -1) return 1"
+. auto/feature

 ngx_feature="sys_nerr"
 ngx_feature_name="NGX_SYS_NERR"
diff -r 6d2e92acb013 -r f955f9cddd38 src/core/ngx_output_chain.c
--- a/src/core/ngx_output_chain.c       Thu Dec 28 12:01:05 2017 +0200
+++ b/src/core/ngx_output_chain.c       Tue Jan 09 14:54:13 2018 +0300
@@ -577,7 +577,15 @@
         } else
 #endif
 #if (NGX_THREADS)
-        if (ctx->thread_handler) {
+#if (NGX_HAVE_PREADV2_NONBLOCK)
+
+        n = ngx_preadv2_file(src->file, dst->pos, (size_t) size,
+                             src->file_pos);
+#else
+        n = NGX_AGAIN;
+#endif
+        if (n == NGX_AGAIN && ctx->thread_handler) {
+
             src->file->thread_task = ctx->thread_task;
             src->file->thread_handler = ctx->thread_handler;
             src->file->thread_ctx = ctx->filter_ctx;
@@ -589,7 +597,7 @@
                 return NGX_AGAIN;
             }

-        } else
+        } else if (!ctx->thread_handler && n == NGX_AGAIN)
 #endif
         {
             n = ngx_read_file(src->file, dst->pos, (size_t) size,
diff -r 6d2e92acb013 -r f955f9cddd38 src/http/ngx_http_file_cache.c
--- a/src/http/ngx_http_file_cache.c    Thu Dec 28 12:01:05 2017 +0200
+++ b/src/http/ngx_http_file_cache.c    Tue Jan 09 14:54:13 2018 +0300
@@ -699,6 +699,19 @@
 #if (NGX_THREADS)

     if (clcf->aio == NGX_HTTP_AIO_THREADS) {
+
+#if (NGX_HAVE_PREADV2_NONBLOCK)
+
+        n = ngx_preadv2_file(&c->file, c->buf->pos, c->body_start, 0);
+
+        if (n != NGX_AGAIN) {
+            ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->file.log, 0,
+                    "preadv2 non blocking: \"%s\" - %uz",
c->file.name.data, c->body_start);
+            return n;
+        }
+
+#endif
+
         c->file.thread_task = c->thread_task;
         c->file.thread_handler = ngx_http_cache_thread_handler;
         c->file.thread_ctx = r;
diff -r 6d2e92acb013 -r f955f9cddd38 src/os/unix/ngx_files.c
--- a/src/os/unix/ngx_files.c   Thu Dec 28 12:01:05 2017 +0200
+++ b/src/os/unix/ngx_files.c   Tue Jan 09 14:54:13 2018 +0300
@@ -26,6 +26,68 @@

 #endif

+#if (NGX_THREADS)
+#if (NGX_HAVE_PREADV2_NONBLOCK)
+
+ngx_uint_t  ngx_preadv2_nonblock = 1;
+
+#endif
+
+ssize_t
+ngx_preadv2_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
+{
+#if (NGX_HAVE_PREADV2_NONBLOCK)
+    ssize_t  n;
+    struct iovec   iovs[1];
+
+    if (!ngx_preadv2_nonblock) {
+        return NGX_AGAIN;
+    }
+
+    iovs[0].iov_base = buf;
+    iovs[0].iov_len = size;
+
+    n = preadv2(file->fd, iovs, 1, offset, RWF_NOWAIT);
+
+    if (n == -1) { // let's analyze the return code
+        switch (ngx_errno) {
+            case EAGAIN:
+                ngx_log_debug(NGX_LOG_DEBUG_CORE, file->log, 0,
+                    "preadv2() will block on \"%s\"", file->name.data);
+                return NGX_AGAIN;
+            case EINVAL:
+                // Most possible case - not supported RWF_NOWAIT
+                ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno,
+                      "preadv2() \"%s\" failed RWF_NOWAIT", file->name.data);
+                ngx_preadv2_nonblock = 0;
+                return NGX_AGAIN;
+            default:
+                return NGX_AGAIN;
+
+        }
+    }
+
+    // Check if we read partial file
+    if (((size_t)n < size) && (n < file->info.st_size)) {
+        // blocked on partial read
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+               "preadv2() blocked partial on \"%s\" "
+               "with read size %uz", file->name.data, n);
+        return NGX_AGAIN;
+    }
+
+    file->offset += n;
+
+    return n;
+
+#else
+
+    return NGX_AGAIN;
+
+#endif
+}
+
+#endif

 ssize_t
 ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
diff -r 6d2e92acb013 -r f955f9cddd38 src/os/unix/ngx_files.h
--- a/src/os/unix/ngx_files.h   Thu Dec 28 12:01:05 2017 +0200
+++ b/src/os/unix/ngx_files.h   Tue Jan 09 14:54:13 2018 +0300
@@ -389,7 +389,12 @@
     off_t offset, ngx_pool_t *pool);
 ssize_t ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl,
     off_t offset, ngx_pool_t *pool);
+
+#if (NGX_HAVE_PREADV2_NONBLOCK)
+ssize_t ngx_preadv2_file(ngx_file_t *file, u_char *buf, size_t size,
+    off_t offset);
 #endif

+#endif

 #endif /* _NGX_FILES_H_INCLUDED_ */


More information about the nginx-devel mailing list