FreeBSD disk AIO support

Igor Sysoev is at rambler-co.ru
Sun Aug 23 20:11:50 MSD 2009


Экспериментальный патч для поддержки дискового AIO под FreeBSD.
Ядро должно поддерживать AIO:

options  AIO
или
kldload aio

Настраивается так:

./configure --with-file-aio ...

location / {
    aio  on;
    output_buffers  1 128k;
}

Возможно, нужно подкрутить следующие sysctl'и:

vfs.aio.max_aio_queue             1024
vfs.aio.max_aio_queue_per_proc    256
vfs.aio.max_aio_per_proc          32
vfs.aio.max_aio_procs             32

Диагностировать текущую AIO загрузку можно с помощью

vfs.aio.num_queue_count
vfs.aio.num_aio_procs

Если ядерных aio процессов не хватает или же в ядре нет поддержки aio
вообще, то nginx переходит к обычному чтению.


-- 
Игорь Сысоев
http://sysoev.ru
-------------- next part --------------
Index: src/http/ngx_http_copy_filter_module.c
===================================================================
--- src/http/ngx_http_copy_filter_module.c	(revision 2356)
+++ src/http/ngx_http_copy_filter_module.c	(working copy)
@@ -11,9 +11,18 @@
 
 typedef struct {
     ngx_bufs_t  bufs;
+#if (NGX_HAVE_FILE_AIO)
+    ngx_flag_t  aio;
+#endif
 } ngx_http_copy_filter_conf_t;
 
 
+#if (NGX_HAVE_FILE_AIO)
+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);
+#endif
+
 static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf);
 static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf,
     void *parent, void *child);
@@ -29,6 +38,17 @@
       offsetof(ngx_http_copy_filter_conf_t, bufs),
       NULL },
 
+#if (NGX_HAVE_FILE_AIO)
+
+    { ngx_string("aio"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_copy_filter_conf_t, aio),
+      NULL },
+
+#endif
+
       ngx_null_command
 };
 
@@ -104,6 +124,12 @@
         ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_filter;
         ctx->filter_ctx = r;
 
+#if (NGX_HAVE_FILE_AIO)
+        if (conf->aio) {
+            ctx->aio = ngx_http_copy_aio_handler;
+        }
+#endif
+
         r->request_output = 1;
     }
 
@@ -125,6 +151,37 @@
 }
 
 
+#if (NGX_HAVE_FILE_AIO)
+
+static void
+ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
+{
+    ngx_http_request_t *r;
+
+    r = ctx->filter_ctx;
+
+    file->aio->event->data = r;
+    file->aio->event->handler = ngx_http_copy_aio_event_handler;
+
+    r->connection->write->blocked = 1;
+}
+
+
+static void
+ngx_http_copy_aio_event_handler(ngx_event_t *ev)
+{
+    ngx_http_request_t  *r;
+
+    r = ev->data;
+
+    r->connection->write->blocked = 0;
+
+    r->connection->write->handler(r->connection->write);
+}
+
+#endif
+
+
 static void *
 ngx_http_copy_filter_create_conf(ngx_conf_t *cf)
 {
@@ -136,6 +193,9 @@
     }
 
     conf->bufs.num = 0;
+#if (NGX_HAVE_FILE_AIO)
+    conf->aio = NGX_CONF_UNSET;
+#endif
 
     return conf;
 }
@@ -148,6 +208,9 @@
     ngx_http_copy_filter_conf_t *conf = child;
 
     ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768);
+#if (NGX_HAVE_FILE_AIO)
+    ngx_conf_merge_value(conf->aio, prev->aio, 0);
+#endif
 
     return NULL;
 }
Index: src/http/ngx_http_request.c
===================================================================
--- src/http/ngx_http_request.c	(revision 2356)
+++ src/http/ngx_http_request.c	(working copy)
@@ -1868,6 +1868,11 @@
             return;
         }
 
+        if (c->write->blocked) {
+            (void) ngx_http_set_write_handler(r);
+            return;
+        }
+
         ngx_http_close_request(r, 0);
         return;
     }
@@ -1966,7 +1971,7 @@
         return;
     }
 
-    if (r->buffered || c->buffered || r->postponed) {
+    if (r->buffered || c->buffered || r->postponed || c->write->blocked) {
 
         if (ngx_http_set_write_handler(r) != NGX_OK) {
             ngx_http_close_request(r, 0);
@@ -2100,7 +2105,7 @@
         }
 
     } else {
-        if (wev->delayed) {
+        if (wev->delayed || wev->blocked) {
             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
                            "http writer delayed");
 
Index: src/event/ngx_event.h
===================================================================
--- src/event/ngx_event.h	(revision 2356)
+++ src/event/ngx_event.h	(working copy)
@@ -67,6 +67,7 @@
     unsigned         timer_set:1;
 
     unsigned         delayed:1;
+    unsigned         blocked:1;
 
     unsigned         read_discarded:1;
 
Index: src/os/unix/ngx_file_aio_read.c
===================================================================
--- src/os/unix/ngx_file_aio_read.c	(revision 0)
+++ src/os/unix/ngx_file_aio_read.c	(revision 0)
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+#if (NGX_HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#endif
+
+
+ssize_t
+ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
+{
+    int                    n;
+    ngx_event_t           *ev;
+    ngx_file_aio_ident_t  *aio;
+
+    aio = file->aio;
+    ev = aio->event;
+
+    if (!ev->ready) {
+        ngx_log_error(NGX_LOG_ALERT, file->log, 0, "second aio post");
+        return NGX_AGAIN;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+                   "complete:%d size:%z", ev->complete, size);
+
+    if (!ev->complete) {
+        aio->aiocb.aio_fildes = file->fd;
+        aio->aiocb.aio_offset = offset;
+        aio->aiocb.aio_buf = buf;
+        aio->aiocb.aio_nbytes = size;
+
+#if (NGX_HAVE_KQUEUE)
+        aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+        aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+        aio->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev;
+#endif
+
+        n = aio_read(&aio->aiocb);
+
+        if (n == -1) {
+            n = ngx_errno;
+
+            if (n == NGX_EAGAIN || n == NGX_ENOSYS) {
+                return NGX_BUSY;
+            }
+
+            ngx_log_error(NGX_LOG_CRIT, file->log, n, "aio_read() failed");
+            return NGX_ERROR;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+                       "aio_read: fd:%d %d", file->fd, n);
+
+        ev->active = 1;
+        ev->ready = 0;
+    }
+
+    ev->complete = 0;
+
+    n = aio_error(&aio->aiocb);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+                   "aio_error: fd:%d %d", file->fd, n);
+
+    if (n == -1) {
+        ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
+                      "aio_error() failed");
+        return NGX_ERROR;
+    }
+
+    if (n != 0) {
+        if (n == NGX_EINPROGRESS) {
+            if (ev->ready) {
+                ngx_log_error(NGX_LOG_ALERT, file->log, n,
+                              "aio_read() still in progress");
+                ev->ready = 0;
+            }
+            return NGX_AGAIN;
+        }
+
+        ngx_log_error(NGX_LOG_CRIT, file->log, n, "aio_read() failed");
+        ev->ready = 0;
+        return NGX_ERROR;
+    }
+
+    n = aio_return(&aio->aiocb);
+
+    if (n == -1) {
+        ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
+                      "aio_return() failed");
+        ev->ready = 0;
+        return NGX_ERROR;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+                   "aio_return: fd:%d %d", file->fd, n);
+
+    if (n == 0) {
+        ev->ready = 0;
+
+    } else {
+        ev->ready = 1;
+    }
+
+    ev->active = 0;
+
+    return n;
+}
Index: src/os/unix/ngx_posix_config.h
===================================================================
--- src/os/unix/ngx_posix_config.h	(revision 2356)
+++ src/os/unix/ngx_posix_config.h	(working copy)
@@ -112,6 +112,11 @@
 #endif
 
 
+#if (NGX_HAVE_FILE_AIO)
+#include <aio.h>
+#endif
+
+
 #define NGX_LISTEN_BACKLOG  511
 
 
Index: src/os/unix/ngx_linux_config.h
===================================================================
--- src/os/unix/ngx_linux_config.h	(revision 2356)
+++ src/os/unix/ngx_linux_config.h	(working copy)
@@ -81,6 +81,11 @@
 #endif
 
 
+#if (NGX_HAVE_FILE_AIO)
+#include <aio.h>
+#endif
+
+
 #define NGX_LISTEN_BACKLOG        511
 
 
Index: src/os/unix/ngx_freebsd_config.h
===================================================================
--- src/os/unix/ngx_freebsd_config.h	(revision 2356)
+++ src/os/unix/ngx_freebsd_config.h	(working copy)
@@ -73,13 +73,13 @@
 #endif
 
 
-#if (NGX_HAVE_AIO)
-#include <aio.h>
+#if (NGX_HAVE_KQUEUE)
+#include <sys/event.h>
 #endif
 
 
-#if (NGX_HAVE_KQUEUE)
-#include <sys/event.h>
+#if (NGX_HAVE_FILE_AIO || NGX_HAVE_AIO)
+#include <aio.h>
 #endif
 
 
Index: src/os/unix/ngx_solaris_config.h
===================================================================
--- src/os/unix/ngx_solaris_config.h	(revision 2356)
+++ src/os/unix/ngx_solaris_config.h	(working copy)
@@ -62,24 +62,24 @@
 #endif
 
 
-#if (NGX_HAVE_SENDFILE)
-#include <sys/sendfile.h>
+#if (NGX_HAVE_DEVPOLL)
+#include <sys/ioctl.h>
+#include <sys/devpoll.h>
 #endif
 
 
-#if (NGX_HAVE_AIO)
-#include <aio.h>
+#if (NGX_HAVE_EVENTPORT)
+#include <port.h>
 #endif
 
 
-#if (NGX_HAVE_DEVPOLL)
-#include <sys/ioctl.h>
-#include <sys/devpoll.h>
+#if (NGX_HAVE_SENDFILE)
+#include <sys/sendfile.h>
 #endif
 
 
-#if (NGX_HAVE_EVENTPORT)
-#include <port.h>
+#if (NGX_HAVE_FILE_AIO)
+#include <aio.h>
 #endif
 
 
Index: src/os/unix/ngx_files.h
===================================================================
--- src/os/unix/ngx_files.h	(revision 2356)
+++ src/os/unix/ngx_files.h	(working copy)
@@ -287,4 +287,12 @@
 #define ngx_set_stderr_n         "dup2(STDERR_FILENO)"
 
 
+#if (NGX_HAVE_FILE_AIO)
+
+ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size,
+    off_t offset);
+
+#endif
+
+
 #endif /* _NGX_FILES_H_INCLUDED_ */
Index: src/core/ngx_output_chain.c
===================================================================
--- src/core/ngx_output_chain.c	(revision 2356)
+++ src/core/ngx_output_chain.c	(working copy)
@@ -28,6 +28,11 @@
 #define NGX_NONE            1
 
 
+#if (NGX_HAVE_FILE_AIO)
+static ssize_t ngx_aio_read_file(ngx_output_chain_ctx_t *ctx, ngx_buf_t *src,
+    ngx_buf_t *dst, size_t size);
+#endif
+
 static ngx_inline ngx_int_t
     ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
 static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
@@ -519,8 +524,30 @@
 
 #endif
 
+#if (NGX_HAVE_FILE_AIO)
+
+        if (ctx->aio) {
+            n = ngx_aio_read_file(ctx, src, dst, size);
+
+            if (n == NGX_AGAIN) {
+                return (ngx_int_t) n;
+            }
+
+        } else {
+            n = NGX_BUSY;
+        }
+
+        if (n == NGX_BUSY) {
+            n = ngx_read_file(src->file, dst->pos, (size_t) size,
+                              src->file_pos);
+        }
+
+#else
+
         n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);
 
+#endif
+
 #if (NGX_HAVE_ALIGNED_DIRECTIO)
 
         if (ctx->unaligned) {
@@ -545,12 +572,6 @@
             return (ngx_int_t) n;
         }
 
-#if (NGX_FILE_AIO_READ)
-        if (n == NGX_AGAIN) {
-            return (ngx_int_t) n;
-        }
-#endif
-
         if (n != size) {
             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
                           ngx_read_file_n " read only %z of %O from \"%s\"",
@@ -585,6 +606,45 @@
 }
 
 
+#if (NGX_HAVE_FILE_AIO)
+
+static ssize_t
+ngx_aio_read_file(ngx_output_chain_ctx_t *ctx, ngx_buf_t *src, ngx_buf_t *dst,
+    size_t size)
+{
+    ssize_t      n;
+    ngx_file_t  *file;
+
+    file = src->file;
+
+    if (file->aio == NULL) {
+        file->aio = ngx_pcalloc(ctx->pool, sizeof(ngx_file_aio_ident_t));
+        if (file->aio == NULL) {
+            return NGX_ERROR;
+        }
+
+        file->aio->event = ngx_pcalloc(ctx->pool, sizeof(ngx_event_t));
+        if (file->aio->event == NULL) {
+            return NGX_ERROR;
+        }
+
+        file->aio->event->ready = 1;
+        file->aio->event->log = file->log;
+        file->aio->fd = file->fd;
+    }
+
+    n = ngx_file_aio_read(src->file, dst->pos, size, src->file_pos);
+
+    if (n == NGX_AGAIN) {
+        ctx->aio(ctx, src->file);
+    }
+
+    return n;
+}
+
+#endif
+
+
 ngx_int_t
 ngx_chain_writer(void *data, ngx_chain_t *in)
 {
Index: src/core/ngx_buf.h
===================================================================
--- src/core/ngx_buf.h	(revision 2356)
+++ src/core/ngx_buf.h	(working copy)
@@ -67,9 +67,16 @@
 } ngx_bufs_t;
 
 
+typedef struct ngx_output_chain_ctx_s  ngx_output_chain_ctx_t;
+
 typedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in);
 
-typedef struct {
+#if (NGX_HAVE_FILE_AIO)
+typedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx,
+    ngx_file_t *file);
+#endif
+
+struct ngx_output_chain_ctx_s {
     ngx_buf_t                   *buf;
     ngx_chain_t                 *in;
     ngx_chain_t                 *free;
@@ -90,9 +97,13 @@
 
     ngx_output_chain_filter_pt   output_filter;
     void                        *filter_ctx;
-} ngx_output_chain_ctx_t;
 
+#if (NGX_HAVE_FILE_AIO)
+    ngx_output_chain_aio_pt      aio;
+#endif
+};
 
+
 typedef struct {
     ngx_chain_t                 *out;
     ngx_chain_t                **last;
Index: src/core/ngx_file.h
===================================================================
--- src/core/ngx_file.h	(revision 2356)
+++ src/core/ngx_file.h	(working copy)
@@ -12,6 +12,22 @@
 #include <ngx_core.h>
 
 
+#if (NGX_HAVE_FILE_AIO)
+
+typedef struct {
+    ngx_connection_t          *connection;
+
+    /* STUB: event is pointer because ngx_event_s definition is incomplete */
+    ngx_event_t               *event;
+    void                      *dummy;
+    ngx_fd_t                   fd;
+
+    struct aiocb               aiocb;
+} ngx_file_aio_ident_t;
+
+#endif
+
+
 struct ngx_file_s {
     ngx_fd_t                   fd;
     ngx_str_t                  name;
@@ -22,10 +38,15 @@
 
     ngx_log_t                 *log;
 
+#if (NGX_HAVE_FILE_AIO)
+    ngx_file_aio_ident_t      *aio;
+#endif
+
     unsigned                   valid_info:1;
     unsigned                   directio:1;
 };
 
+
 #define NGX_MAX_PATH_LEVEL  3
 
 
Index: auto/os/features
===================================================================
--- auto/os/features	(revision 2356)
+++ auto/os/features	(working copy)
@@ -274,3 +274,21 @@
         CORE_LIBS="$CORE_LIBS -lrt"
     fi
 fi
+
+
+if [ $NGX_FILE_AIO = YES ]; then
+    ngx_feature="kqueue AIO support"
+    ngx_feature_name="NGX_HAVE_FILE_AIO"
+    ngx_feature_run=no
+    ngx_feature_incs="#include <aio.h>"
+    ngx_feature_path=
+    ngx_feature_libs=
+    ngx_feature_test="int  n; struct aiocb  iocb;
+                      iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+                      n = aio_read(&iocb)"
+    . auto/feature
+
+    if [ $ngx_found = yes ]; then
+        CORE_SRCS="$CORE_SRCS $FILE_AIO_SRCS"
+    fi
+fi
Index: auto/sources
===================================================================
--- auto/sources	(revision 2356)
+++ auto/sources	(working copy)
@@ -125,6 +125,7 @@
           src/os/unix/ngx_aio_read_chain.c \
           src/os/unix/ngx_aio_write_chain.c"
 
+FILE_AIO_SRCS="src/os/unix/ngx_file_aio_read.c"
 
 UNIX_INCS="$CORE_INCS $EVENT_INCS src/os/unix"
 
Index: auto/options
===================================================================
--- auto/options	(revision 2356)
+++ auto/options	(working copy)
@@ -43,6 +43,7 @@
 
 USE_THREADS=NO
 
+NGX_FILE_AIO=NO
 NGX_IPV6=NO
 
 HTTP=YES
@@ -170,6 +171,7 @@
         #--with-threads=*)                USE_THREADS="$value"       ;;
         #--with-threads)                  USE_THREADS="pthreads"     ;;
 
+        --with-file-aio)                 NGX_FILE_AIO=YES           ;;
         --with-ipv6)                     NGX_IPV6=YES               ;;
 
         --without-http)                  HTTP=NO                    ;;
@@ -305,6 +307,7 @@
   --with-poll_module                 enable poll module
   --without-poll_module              disable poll module
 
+  --with-file-aio                    enable file aio support
   --with-ipv6                        enable ipv6 support
 
   --with-http_ssl_module             enable ngx_http_ssl_module


More information about the nginx-ru mailing list