[nginx] Mp4: of.directio_off flag to avoid unneeded syscalls.

2025-05-25 Thread Maxim Dounin
details:   http://freenginx.org/hg/nginx/rev/268e0fb3a16c
branches:  
changeset: 9360:268e0fb3a16c
user:  Maxim Dounin 
date:  Sun May 25 23:57:39 2025 +0300
description:
Mp4: of.directio_off flag to avoid unneeded syscalls.

When set, ngx_open_cached_file() won't try to enable directio on the file,
allowing the caller to do it itself, but will set is_directio flag in
ngx_open_file_info_t and in open file cache.  The fact that directio needs
to be enabled by the caller is signalled in the of.is_directio_off output
flag.

This approach makes it possible to avoid unneeded syscalls when directio
is enabled, yet the caller needs it disabled for some initial file
processing, such as in the mp4 module.

diffstat:

 src/core/ngx_open_file_cache.c |   8 +++-
 src/core/ngx_open_file_cache.h |   2 ++
 src/http/modules/ngx_http_mp4_module.c |  10 +-
 3 files changed, 14 insertions(+), 6 deletions(-)

diffs (105 lines):

diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -249,6 +249,7 @@ ngx_open_cached_file(ngx_open_file_cache
 of->is_link = file->is_link;
 of->is_exec = file->is_exec;
 of->is_directio = file->is_directio;
+of->is_directio_off = 0;
 
 if (!file->is_dir) {
 file->count++;
@@ -313,6 +314,7 @@ ngx_open_cached_file(ngx_open_file_cache
 }
 
 of->is_directio = file->is_directio;
+of->is_directio_off = 0;
 
 goto update;
 }
@@ -920,7 +922,11 @@ ngx_open_and_stat_file(ngx_str_t *name, 
 }
 
 if (of->directio <= ngx_file_size(&fi)) {
-if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
+if (of->directio_off) {
+of->is_directio = 1;
+of->is_directio_off = 1;
+
+} else if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
   ngx_directio_on_n " \"%V\" failed", name);
 
diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -42,12 +42,14 @@ typedef struct {
 unsigned log:1;
 unsigned errors:1;
 unsigned events:1;
+unsigned directio_off:1;
 
 unsigned is_dir:1;
 unsigned is_file:1;
 unsigned is_link:1;
 unsigned is_exec:1;
 unsigned is_directio:1;
+unsigned is_directio_off:1;
 } ngx_open_file_info_t;
 
 
diff --git a/src/http/modules/ngx_http_mp4_module.c 
b/src/http/modules/ngx_http_mp4_module.c
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -479,7 +479,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 u_char*last;
 size_t root;
 ngx_int_t  rc, start, end;
-ngx_uint_t level, length, directio;
+ngx_uint_t level, length;
 ngx_str_t  path, value;
 ngx_log_t *log;
 ngx_buf_t *b;
@@ -520,6 +520,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 
 of.read_ahead = clcf->read_ahead;
 of.directio = clcf->directio;
+of.directio_off = 1;
 of.valid = clcf->open_file_cache_valid;
 of.min_uses = clcf->open_file_cache_min_uses;
 of.errors = clcf->open_file_cache_errors;
@@ -579,7 +580,6 @@ ngx_http_mp4_handler(ngx_http_request_t 
 
 start = -1;
 length = 0;
-directio = 0;
 r->headers_out.content_length_n = of.size;
 mp4 = NULL;
 b = NULL;
@@ -615,7 +615,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 if (start >= 0) {
 r->single_range = 1;
 
-if (of.is_directio) {
+if (of.is_directio && !of.is_directio_off) {
 
 /*
  * DIRECTIO is set on transfer only
@@ -627,7 +627,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
   ngx_directio_off_n " \"%s\" failed", path.data);
 }
 
-directio = 1;
+of.is_directio_off = 1;
 }
 
 mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
@@ -673,7 +673,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 
 log->action = "sending mp4 to client";
 
-if (directio) {
+if (of.is_directio_off) {
 
 /* DIRECTIO was switched off, restore it */
 


[nginx] Mp4: fixed directio usage with open_file_cache.

2025-05-25 Thread Maxim Dounin
details:   http://freenginx.org/hg/nginx/rev/314192d67a91
branches:  
changeset: 9359:314192d67a91
user:  Maxim Dounin 
date:  Sun May 25 23:57:28 2025 +0300
description:
Mp4: fixed directio usage with open_file_cache.

With open_file_cache, if directio is enabled on a file descriptor after
opening the file, other consumers won't know about directio being enabled
and will use unaligned reads, leading to EINVAL errors from pread()
on Linux.

Further, if a file descriptor with directio enabled will be returned to
the mp4 module itself, during mp4 file processing it will be used in
assumption that directio is not enabled.

Fix is to use of.directio and ngx_open_cached_file() to enable directio,
and switch it off during mp4 file processing.

While this does some unneeded syscalls if the file is actually just opened
and we have to parse the file, this shouldn't be significant compared to
other mp4 file processing costs.

diffstat:

 src/http/modules/ngx_http_mp4_module.c |  36 -
 1 files changed, 22 insertions(+), 14 deletions(-)

diffs (86 lines):

diff --git a/src/http/modules/ngx_http_mp4_module.c 
b/src/http/modules/ngx_http_mp4_module.c
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -479,7 +479,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 u_char*last;
 size_t root;
 ngx_int_t  rc, start, end;
-ngx_uint_t level, length;
+ngx_uint_t level, length, directio;
 ngx_str_t  path, value;
 ngx_log_t *log;
 ngx_buf_t *b;
@@ -519,7 +519,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 ngx_memzero(&of, sizeof(ngx_open_file_info_t));
 
 of.read_ahead = clcf->read_ahead;
-of.directio = NGX_MAX_OFF_T_VALUE;
+of.directio = clcf->directio;
 of.valid = clcf->open_file_cache_valid;
 of.min_uses = clcf->open_file_cache_min_uses;
 of.errors = clcf->open_file_cache_errors;
@@ -579,6 +579,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 
 start = -1;
 length = 0;
+directio = 0;
 r->headers_out.content_length_n = of.size;
 mp4 = NULL;
 b = NULL;
@@ -614,6 +615,21 @@ ngx_http_mp4_handler(ngx_http_request_t 
 if (start >= 0) {
 r->single_range = 1;
 
+if (of.is_directio) {
+
+/*
+ * DIRECTIO is set on transfer only
+ * to allow kernel to cache "moov" atom
+ */
+
+if (ngx_directio_off(of.fd) == NGX_FILE_ERROR) {
+ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+  ngx_directio_off_n " \"%s\" failed", path.data);
+}
+
+directio = 1;
+}
+
 mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
 if (mp4 == NULL) {
 return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -622,6 +638,7 @@ ngx_http_mp4_handler(ngx_http_request_t 
 mp4->file.fd = of.fd;
 mp4->file.name = path;
 mp4->file.log = r->connection->log;
+mp4->file.directio = of.is_directio;
 mp4->end = of.size;
 mp4->start = (ngx_uint_t) start;
 mp4->length = length;
@@ -656,23 +673,14 @@ ngx_http_mp4_handler(ngx_http_request_t 
 
 log->action = "sending mp4 to client";
 
-if (clcf->directio <= of.size) {
-
-/*
- * DIRECTIO is set on transfer only
- * to allow kernel to cache "moov" atom
- */
+if (directio) {
+
+/* DIRECTIO was switched off, restore it */
 
 if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {
 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
   ngx_directio_on_n " \"%s\" failed", path.data);
 }
-
-of.is_directio = 1;
-
-if (mp4) {
-mp4->file.directio = 1;
-}
 }
 
 r->headers_out.status = NGX_HTTP_OK;


[nginx-tests] Tests: mp4 tests with directio and open_file_cache.

2025-05-25 Thread Maxim Dounin
details:   http://freenginx.org/hg/nginx-tests/rev/ba4f76b3b823
branches:  
changeset: 2008:ba4f76b3b823
user:  Maxim Dounin 
date:  Mon May 26 00:02:51 2025 +0300
description:
Tests: mp4 tests with directio and open_file_cache.

diffstat:

 mp4_directio.t |  78 ++
 1 files changed, 78 insertions(+), 0 deletions(-)

diffs (83 lines):

diff --git a/mp4_directio.t b/mp4_directio.t
new file mode 100644
--- /dev/null
+++ b/mp4_directio.t
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for directio support in mp4 module.
+
+###
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http mp4/)->has_daemon('ffmpeg')
+   ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+%%TEST_GLOBALS_HTTP%%
+
+server {
+listen   127.0.0.1:8080;
+server_name  localhost;
+
+location / {
+mp4;
+open_file_cache max=10;
+directio 0;
+}
+}
+}
+
+EOF
+
+plan(skip_all => 'no lavfi')
+   unless grep /lavfi/, `ffmpeg -loglevel quiet -formats`;
+plan(skip_all => 'no libx264 or libopenh264')
+   unless grep /libx264|libopenh264/, `ffmpeg -loglevel quiet -encoders`;
+system('ffmpeg -nostdin -loglevel quiet -y '
+   . '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 '
+   . '-g 15 -c:v h264 '
+   . "${\($t->testdir())}/test.mp4") == 0
+   or die "Can't create mp4 file: $!";
+
+$t->run()->plan(2);
+
+###
+
+# mp4 module uses unaligned reads while parsing mp4 file, though
+# failed to disable directio if a file with directio enabled was
+# returned from open file cache
+
+like(http_get('/test.mp4?start=1.0'), qr/ 200 /, 'mp4 directio first');
+
+TODO: {
+local $TODO = 'not yet' unless $t->has_version('1.29.0') or $^O ne 'linux';
+
+like(http_get('/test.mp4?start=1.0'), qr/ 200 /, 'mp4 directio cached');
+
+}
+
+###


[nginx] Cache: directio support when reading cache files.

2025-05-25 Thread Maxim Dounin
details:   http://freenginx.org/hg/nginx/rev/e2a6fefd81db
branches:  
changeset: 9361:e2a6fefd81db
user:  Maxim Dounin 
date:  Mon May 26 00:04:45 2025 +0300
description:
Cache: directio support when reading cache files.

diffstat:

 src/http/ngx_http_file_cache.c |  32 
 1 files changed, 28 insertions(+), 4 deletions(-)

diffs (60 lines):

diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -353,7 +353,7 @@ ngx_http_file_cache_open(ngx_http_reques
 of.valid = clcf->open_file_cache_valid;
 of.min_uses = clcf->open_file_cache_min_uses;
 of.events = clcf->open_file_cache_events;
-of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
+of.directio = clcf->directio;
 of.read_ahead = clcf->read_ahead;
 
 if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, 
r->pool)
@@ -380,13 +380,36 @@ ngx_http_file_cache_open(ngx_http_reques
 
 c->file.fd = of.fd;
 c->file.log = r->connection->log;
+c->file.directio = of.is_directio;
 c->uniq = of.uniq;
 c->length = of.size;
 c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;
 
-c->buf = ngx_create_temp_buf(r->pool, c->body_start);
-if (c->buf == NULL) {
-return NGX_ERROR;
+if (of.is_directio) {
+
+c->body_start = ngx_align(c->body_start, clcf->directio_alignment);
+
+c->buf = ngx_calloc_buf(r->pool);
+if (c->buf == NULL) {
+return NGX_ERROR;
+}
+
+c->buf->start = ngx_pmemalign(r->pool, c->body_start,
+  clcf->directio_alignment);
+if (c->buf->start == NULL) {
+return NGX_ERROR;
+}
+
+c->buf->pos = c->buf->start;
+c->buf->last = c->buf->start;
+c->buf->end = c->buf->start + c->body_start;
+c->buf->temporary = 1;
+
+} else {
+c->buf = ngx_create_temp_buf(r->pool, c->body_start);
+if (c->buf == NULL) {
+return NGX_ERROR;
+}
 }
 
 return ngx_http_file_cache_read(r, c);
@@ -1663,6 +1686,7 @@ ngx_http_cache_send(ngx_http_request_t *
 b->file->fd = c->file.fd;
 b->file->name = c->file.name;
 b->file->log = r->connection->log;
+b->file->directio = c->file.directio;
 
 out.buf = b;
 out.next = NULL;


[PATCH 0 of 5] open file cache fixes

2025-05-25 Thread Maxim Dounin
Hello!

The following patch series implements some open file cache fixes.
Notably, directio handling is fixed to be compatible with threaded IO.

-- 
Maxim Dounin



[PATCH 1 of 5] Disabled open_file_cache on platforms without pread()

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748208420 -10800
#  Mon May 26 00:27:00 2025 +0300
# Node ID 811bf7dc0d46e5e6d69e700a1a5a3852d4ba0381
# Parent  e2a6fefd81dbe201a88876a95f34620df87259a2
Disabled open_file_cache on platforms without pread().

Current open file cache code cannot properly work on platforms without
pread(), since file->sys_offset is not shared across files.  Further, it
is not set on file initialization after ngx_open_cached_file(), leading
to incorrect value 0 instead of non-zero current offset for cached file
descriptors.

Since platforms without pread() are rather exotic nowadays, fix is to
disable open_file_cache for them.

diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -4981,6 +4981,8 @@ ngx_http_core_error_page(ngx_conf_t *cf,
 static char *
 ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
+#if (NGX_HAVE_PREAD || NGX_WIN32)
+
 ngx_http_core_loc_conf_t *clcf = conf;
 
 time_t   inactive;
@@ -5048,11 +5050,17 @@ ngx_http_core_open_file_cache(ngx_conf_t
 }
 
 clcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
-if (clcf->open_file_cache) {
-return NGX_CONF_OK;
-}
-
-return NGX_CONF_ERROR;
+if (clcf->open_file_cache == NULL) {
+return NGX_CONF_ERROR;
+}
+
+#else
+ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+   "\"open_file_cache\" is not supported "
+   "on this platform, ignored");
+#endif
+
+return NGX_CONF_OK;
 }
 
 



[PATCH 2 of 5] Open file cache: style

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748208424 -10800
#  Mon May 26 00:27:04 2025 +0300
# Node ID 8cc90a3e3e74d31a92b2732f6f4003684a011fcd
# Parent  811bf7dc0d46e5e6d69e700a1a5a3852d4ba0381
Open file cache: style.

diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -974,7 +974,7 @@ ngx_open_file_add_event(ngx_open_file_ca
 file->use_event = 0;
 
 file->event = ngx_calloc(sizeof(ngx_event_t), log);
-if (file->event== NULL) {
+if (file->event == NULL) {
 return;
 }
 
diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h
--- a/src/core/ngx_open_file_cache.h
+++ b/src/core/ngx_open_file_cache.h
@@ -53,9 +53,7 @@ typedef struct {
 } ngx_open_file_info_t;
 
 
-typedef struct ngx_cached_open_file_s  ngx_cached_open_file_t;
-
-struct ngx_cached_open_file_s {
+typedef struct {
 ngx_rbtree_node_tnode;
 ngx_queue_t  queue;
 
@@ -87,7 +85,7 @@ struct ngx_cached_open_file_s {
 unsigned is_directio:1;
 
 ngx_event_t *event;
-};
+} ngx_cached_open_file_t;
 
 
 typedef struct {



[PATCH 3 of 5] Open file cache: disable_symlinks with open_file_cache_events

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748208427 -10800
#  Mon May 26 00:27:07 2025 +0300
# Node ID f84080a83901176c0dd95681b19290f070de045c
# Parent  8cc90a3e3e74d31a92b2732f6f4003684a011fcd
Open file cache: disable_symlinks with open_file_cache_events.

Previously, different disable_symlinks settings were respected when
retrieving a file from open file cache when using periodic retest, but
were ignored with using open_file_cache_events.

Fix is to test disable_symlinks settings in all cases, and retest the
file if requested settings are different from what we already have in
the cache.

diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -227,15 +227,15 @@ ngx_open_cached_file(ngx_open_file_cache
 goto add_event;
 }
 
-if (file->use_event
-|| (file->event == NULL
-&& (of->uniq == 0 || of->uniq == file->uniq)
-&& now - file->created < of->valid
+if ((file->use_event
+ || (file->event == NULL
+ && (of->uniq == 0 || of->uniq == file->uniq)
+ && now - file->created < of->valid))
 #if (NGX_HAVE_OPENAT)
-&& of->disable_symlinks == file->disable_symlinks
-&& of->disable_symlinks_from == file->disable_symlinks_from
+&& of->disable_symlinks == file->disable_symlinks
+&& of->disable_symlinks_from == file->disable_symlinks_from
 #endif
-))
+)
 {
 if (file->err == 0) {
 



[PATCH 4 of 5] Open file cache: fixed file->uses loss on retest

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748208430 -10800
#  Mon May 26 00:27:10 2025 +0300
# Node ID 6032949667f1e5fda9ce364d6eb1668074274c56
# Parent  f84080a83901176c0dd95681b19290f070de045c
Open file cache: fixed file->uses loss on retest.

If an open file was reopened during a retest, but was in use by another
request, the cache entry was re-created with file->uses set to 1.  Fix
is to preserve existing file->uses.

diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -147,6 +147,7 @@ ngx_open_cached_file(ngx_open_file_cache
 time_t  now;
 uint32_thash;
 ngx_int_t   rc;
+ngx_uint_t  uses;
 ngx_file_info_t fi;
 ngx_pool_cleanup_t *cln;
 ngx_cached_open_file_t *file;
@@ -348,6 +349,8 @@ ngx_open_cached_file(ngx_open_file_cache
 
 file->close = 1;
 
+uses = file->uses;
+
 goto create;
 }
 
@@ -359,6 +362,8 @@ ngx_open_cached_file(ngx_open_file_cache
 goto failed;
 }
 
+uses = 1;
+
 create:
 
 if (cache->current >= cache->max) {
@@ -387,7 +392,7 @@ create:
 
 cache->current++;
 
-file->uses = 1;
+file->uses = uses;
 file->count = 0;
 file->use_event = 0;
 file->event = NULL;



[PATCH 5 of 5] Open file cached: correct directio handling with threads

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748208437 -10800
#  Mon May 26 00:27:17 2025 +0300
# Node ID e3e07705481df3c60a3bac8af9101323ee7ceb83
# Parent  6032949667f1e5fda9ce364d6eb1668074274c56
Open file cached: correct directio handling with threads.

On Linux with open_file_cache and directio enabled along with threaded IO,
if a file descriptor is shared among multiple requests, one request might
unexpectedly switch directio on while another request does an unaligned
read in a thread, leading to EINVAL from pread().

To fix this, now all directio changes are done with reference counting
when open file cache is used, and directio is only re-enabled on a file
descriptor when there are no outstanding requests to switch it off.

diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -41,7 +41,8 @@ static void ngx_open_file_add_event(ngx_
 ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
 static void ngx_open_file_cleanup(void *data);
 static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
-ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
+ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_uint_t directio_off,
+ngx_log_t *log);
 static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
 static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
 ngx_uint_t n, ngx_log_t *log);
@@ -51,6 +52,8 @@ static ngx_cached_open_file_t *
 ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
 uint32_t hash);
 static void ngx_open_file_cache_remove(ngx_event_t *ev);
+static ngx_open_file_cache_cleanup_t *
+ngx_open_file_cache_get_cleanup(ngx_pool_t *p, ngx_fd_t fd);
 
 
 ngx_open_file_cache_t *
@@ -118,7 +121,7 @@ ngx_open_file_cache_cleanup(void *data)
 if (!file->err && !file->is_dir) {
 file->close = 1;
 file->count = 0;
-ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
+ngx_close_cached_file(cache, file, 0, 0, ngx_cycle->log);
 
 } else {
 ngx_free(file->name);
@@ -396,6 +399,7 @@ create:
 file->count = 0;
 file->use_event = 0;
 file->event = NULL;
+file->directio_off = of->is_directio_off;
 
 add_event:
 
@@ -449,6 +453,7 @@ found:
 ofcln->cache = cache;
 ofcln->file = file;
 ofcln->min_uses = of->min_uses;
+ofcln->directio_off = of->is_directio_off;
 ofcln->log = pool->log;
 }
 
@@ -1032,7 +1037,8 @@ ngx_open_file_cleanup(void *data)
 
 c->file->count--;
 
-ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
+ngx_close_cached_file(c->cache, c->file, c->min_uses, c->directio_off,
+  c->log);
 
 /* drop one or two expired open files */
 ngx_expire_old_cached_files(c->cache, 1, c->log);
@@ -1041,12 +1047,25 @@ ngx_open_file_cleanup(void *data)
 
 static void
 ngx_close_cached_file(ngx_open_file_cache_t *cache,
-ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
+ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_uint_t directio_off,
+ngx_log_t *log)
 {
 ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
"close cached open file: %s, fd:%d, c:%d, u:%d, %d",
file->name, file->fd, file->count, file->uses, file->close);
 
+if (directio_off) {
+file->directio_off--;
+
+if (file->directio_off == 0) {
+if (ngx_directio_on(file->fd) == NGX_FILE_ERROR) {
+ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+  ngx_directio_on_n " \"%s\" failed",
+  file->name);
+}
+}
+}
+
 if (!file->close) {
 
 file->accessed = ngx_time();
@@ -1143,7 +1162,7 @@ ngx_expire_old_cached_files(ngx_open_fil
 
 if (!file->err && !file->is_dir) {
 file->close = 1;
-ngx_close_cached_file(cache, file, 0, log);
+ngx_close_cached_file(cache, file, 0, 0, log);
 
 } else {
 ngx_free(file->name);
@@ -1255,10 +1274,92 @@ ngx_open_file_cache_remove(ngx_event_t *
 
 file->close = 1;
 
-ngx_close_cached_file(fev->cache, file, 0, ev->log);
+ngx_close_cached_file(fev->cache, file, 0, 0, ev->log);
 
 /* free memory only when fev->cache and fev->file are already not needed */
 
 ngx_free(ev->data);
 ngx_free(ev);
 }
+
+
+ngx_int_t
+ngx_open_file_directio_on(ngx_fd_t fd, ngx_pool_t *pool)
+{
+ngx_open_file_cache_cleanup_t  *c;
+
+/*
+ * DIRECTIO is only re-enabled on a file descriptor
+ * when there are no outstanding requests to switch it off
+ */
+
+c = ngx_open_file_cache_get_cleanup(pool, fd);
+
+if (c) {
+if (!c->directio_off) {
+return NGX_OK;
+}
+
+c->directio_off = 0;
+

[PATCH 3 of 6] Cleaned up iov_base, msg_name, msg_control type casts

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748209475 -10800
#  Mon May 26 00:44:35 2025 +0300
# Node ID 4eed7851024b4ca5d77107676025e84324e23c74
# Parent  38f1f374523ff2630e59f6a722fd6f9d7f70ac01
Cleaned up iov_base, msg_name, msg_control type casts.

In old OSes iov_base, msg_name, and msg_control fields were caddr_t
("char *" in case of iov_base), though later were changed to "void *".
In particular, in FreeBSD this change happened around FreeBSD 4.0.

With newer "void *" type no type casts are needed, but with caddr_t
type casts were required in most cases.

Previously, casts were used inconsistently across the code: to caddr_t,
to "void *", or no casts at all in some recent code.  With this change,
casts to "void *" are consistently used in all relevant places.

diff --git a/src/event/ngx_event_udp.c b/src/event/ngx_event_udp.c
--- a/src/event/ngx_event_udp.c
+++ b/src/event/ngx_event_udp.c
@@ -70,14 +70,14 @@ ngx_event_recvmsg(ngx_event_t *ev)
 iov[0].iov_base = (void *) buffer;
 iov[0].iov_len = sizeof(buffer);
 
-msg.msg_name = &sa;
+msg.msg_name = (void *) &sa;
 msg.msg_namelen = sizeof(ngx_sockaddr_t);
 msg.msg_iov = iov;
 msg.msg_iovlen = 1;
 
 #if (NGX_HAVE_ADDRINFO_CMSG)
 if (ls->wildcard) {
-msg.msg_control = &msg_control;
+msg.msg_control = (void *) &msg_control;
 msg.msg_controllen = sizeof(msg_control);
 
 ngx_memzero(&msg_control, sizeof(msg_control));
@@ -108,7 +108,7 @@ ngx_event_recvmsg(ngx_event_t *ev)
 }
 #endif
 
-sockaddr = msg.msg_name;
+sockaddr = (void *) msg.msg_name;
 socklen = msg.msg_namelen;
 
 if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
diff --git a/src/event/quic/ngx_event_quic_output.c 
b/src/event/quic/ngx_event_quic_output.c
--- a/src/event/quic/ngx_event_quic_output.c
+++ b/src/event/quic/ngx_event_quic_output.c
@@ -410,16 +410,16 @@ ngx_quic_send_segments(ngx_connection_t 
 ngx_memzero(&msg, sizeof(struct msghdr));
 ngx_memzero(msg_control, sizeof(msg_control));
 
+iov.iov_base = (void *) buf;
 iov.iov_len = len;
-iov.iov_base = buf;
 
 msg.msg_iov = &iov;
 msg.msg_iovlen = 1;
 
-msg.msg_name = sockaddr;
+msg.msg_name = (void *) sockaddr;
 msg.msg_namelen = socklen;
 
-msg.msg_control = msg_control;
+msg.msg_control = (void *) msg_control;
 msg.msg_controllen = sizeof(msg_control);
 
 cmsg = CMSG_FIRSTHDR(&msg);
@@ -698,19 +698,19 @@ ngx_quic_send(ngx_connection_t *c, u_cha
 
 ngx_memzero(&msg, sizeof(struct msghdr));
 
+iov.iov_base = (void *) buf;
 iov.iov_len = len;
-iov.iov_base = buf;
 
 msg.msg_iov = &iov;
 msg.msg_iovlen = 1;
 
-msg.msg_name = sockaddr;
+msg.msg_name = (void *) sockaddr;
 msg.msg_namelen = socklen;
 
 #if (NGX_HAVE_ADDRINFO_CMSG)
 if (c->listening && c->listening->wildcard && c->local_sockaddr) {
 
-msg.msg_control = msg_control;
+msg.msg_control = (void *) msg_control;
 msg.msg_controllen = sizeof(msg_control);
 ngx_memzero(msg_control, sizeof(msg_control));
 
diff --git a/src/event/quic/ngx_event_quic_udp.c 
b/src/event/quic/ngx_event_quic_udp.c
--- a/src/event/quic/ngx_event_quic_udp.c
+++ b/src/event/quic/ngx_event_quic_udp.c
@@ -68,14 +68,14 @@ ngx_quic_recvmsg(ngx_event_t *ev)
 iov[0].iov_base = (void *) buffer;
 iov[0].iov_len = sizeof(buffer);
 
-msg.msg_name = &sa;
+msg.msg_name = (void *) &sa;
 msg.msg_namelen = sizeof(ngx_sockaddr_t);
 msg.msg_iov = iov;
 msg.msg_iovlen = 1;
 
 #if (NGX_HAVE_ADDRINFO_CMSG)
 if (ls->wildcard) {
-msg.msg_control = &msg_control;
+msg.msg_control = (void *) &msg_control;
 msg.msg_controllen = sizeof(msg_control);
 
 ngx_memzero(&msg_control, sizeof(msg_control));
@@ -106,7 +106,7 @@ ngx_quic_recvmsg(ngx_event_t *ev)
 }
 #endif
 
-sockaddr = msg.msg_name;
+sockaddr = (void *) msg.msg_name;
 socklen = msg.msg_namelen;
 
 if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
diff --git a/src/os/unix/ngx_channel.c b/src/os/unix/ngx_channel.c
--- a/src/os/unix/ngx_channel.c
+++ b/src/os/unix/ngx_channel.c
@@ -31,7 +31,7 @@ ngx_write_channel(ngx_socket_t s, ngx_ch
 msg.msg_controllen = 0;
 
 } else {
-msg.msg_control = (caddr_t) &cmsg;
+msg.msg_control = (void *) &cmsg;
 msg.msg_controllen = sizeof(cmsg);
 
 ngx_memzero(&cmsg, sizeof(cmsg));
@@ -68,7 +68,7 @@ ngx_write_channel(ngx_socket_t s, ngx_ch
 
 #endif
 
-iov[0].iov_base = (char *) ch;
+iov[0].iov_base = (void *) ch;
 iov[0].iov_len = size;
 
 msg.msg_name = NULL;
@@ -109,7 +109,7 @@ ngx_read_channel(ngx_socket_t s, ngx_cha
 int fd;
 #endif
 
-iov[0].iov_base = (char *) ch;
+iov[0].iov_base = (void *) ch;
 iov[0].iov_len =

[PATCH 5 of 6] Fixed unused variable in ngx_set_srcaddr_cmsg() on MINIX

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748209507 -10800
#  Mon May 26 00:45:07 2025 +0300
# Node ID 7a595fa6ade10f801f6e9bc84a7fd8fc1f9cca21
# Parent  3e11162ed39312387fc79a731071b0a50aa15acc
Fixed unused variable in ngx_set_srcaddr_cmsg() on MINIX.

diff --git a/src/os/unix/ngx_udp_sendmsg_chain.c 
b/src/os/unix/ngx_udp_sendmsg_chain.c
--- a/src/os/unix/ngx_udp_sendmsg_chain.c
+++ b/src/os/unix/ngx_udp_sendmsg_chain.c
@@ -245,7 +245,10 @@ ngx_sendmsg_vec(ngx_connection_t *c, ngx
 size_t
 ngx_set_srcaddr_cmsg(struct cmsghdr *cmsg, struct sockaddr *local_sockaddr)
 {
+#if (NGX_HAVE_IP_SENDSRCADDR || NGX_HAVE_IP_PKTINFO   \
+ || (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO))
 size_tlen;
+#endif
 #if (NGX_HAVE_IP_SENDSRCADDR)
 struct in_addr   *addr;
 struct sockaddr_in   *sin;



[PATCH 1 of 6] Configure: fixed nobody/nogroup detection stderr redirection

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748209461 -10800
#  Mon May 26 00:44:21 2025 +0300
# Node ID 03f5c84e7e8c05ce16f6f36452e4cb818c85dde6
# Parent  e2a6fefd81dbe201a88876a95f34620df87259a2
Configure: fixed nobody/nogroup detection stderr redirection.

Previously, "grep: /etc/group: No such file or directory" errors were
printed on systems without the /etc/group file along with the configure
output.  Fix is to use correct stderr redirection.

diff --git a/auto/unix b/auto/unix
--- a/auto/unix
+++ b/auto/unix
@@ -7,13 +7,13 @@ NGX_USER=${NGX_USER:-nobody}
 
 if [ -z "$NGX_GROUP" ]; then
 if [ $NGX_USER = nobody ]; then
-if grep nobody /etc/group 2>&1 >/dev/null; then
+if grep nobody /etc/group >/dev/null 2>&1; then
 echo "checking for nobody group ... found"
 NGX_GROUP=nobody
 else
 echo "checking for nobody group ... not found"
 
-if grep nogroup /etc/group 2>&1 >/dev/null; then
+if grep nogroup /etc/group >/dev/null 2>&1; then
 echo "checking for nogroup group ... found"
 NGX_GROUP=nogroup
 else



[PATCH 6 of 6] Compatibility with systems without SA_SIGINFO in sigaction()

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748209526 -10800
#  Mon May 26 00:45:26 2025 +0300
# Node ID 95e5cc6c73d1b94a59c4eea26bd2956a1cdc2f3d
# Parent  7a595fa6ade10f801f6e9bc84a7fd8fc1f9cca21
Compatibility with systems without SA_SIGINFO in sigaction().

This change restores compatibility with some older systems without
SA_SIGINFO support, broken by 6985:23ecffd5bcfe.  Notably, this fixes
compilation on MINIX.

diff --git a/auto/unix b/auto/unix
--- a/auto/unix
+++ b/auto/unix
@@ -1043,3 +1043,16 @@ ngx_feature_test='struct addrinfo *res;
   if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 
1;
   freeaddrinfo(res)'
 . auto/feature
+
+
+ngx_feature="sigaction(SA_SIGINFO)"
+ngx_feature_name="NGX_HAVE_SIGINFO"
+ngx_feature_run=no
+ngx_feature_incs="#include "
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct sigaction sa;
+  sa.sa_flags = SA_SIGINFO;
+  sa.sa_sigaction = NULL;
+  if (sigaction(0, &sa, NULL) == -1) return 1;"
+. auto/feature
diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -15,13 +15,21 @@ typedef struct {
 int signo;
 char   *signame;
 char   *name;
+#if (NGX_HAVE_SIGINFO)
 void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
+#else
+void  (*handler)(int signo);
+#endif
 } ngx_signal_t;
 
 
 
 static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
+#if (NGX_HAVE_SIGINFO)
 static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext);
+#else
+static void ngx_signal_handler(int signo);
+#endif
 static void ngx_process_get_status(void);
 static void ngx_unlock_mutexes(ngx_pid_t pid);
 
@@ -291,8 +299,12 @@ ngx_init_signals(ngx_log_t *log)
 ngx_memzero(&sa, sizeof(struct sigaction));
 
 if (sig->handler) {
+#if (NGX_HAVE_SIGINFO)
 sa.sa_sigaction = sig->handler;
 sa.sa_flags = SA_SIGINFO;
+#else
+sa.sa_handler = sig->handler;
+#endif
 
 } else {
 sa.sa_handler = SIG_IGN;
@@ -315,8 +327,13 @@ ngx_init_signals(ngx_log_t *log)
 }
 
 
+#if (NGX_HAVE_SIGINFO)
 static void
 ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
+#else
+static void
+ngx_signal_handler(int signo)
+#endif
 {
 char*action;
 ngx_int_tignore;
@@ -441,12 +458,15 @@ ngx_signal_handler(int signo, siginfo_t 
 break;
 }
 
+#if (NGX_HAVE_SIGINFO)
 if (siginfo && siginfo->si_pid) {
 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
   "signal %d (%s) received from %P%s",
   signo, sig->signame, siginfo->si_pid, action);
 
-} else {
+} else
+#endif
+{
 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
   "signal %d (%s) received%s",
   signo, sig->signame, action);



[PATCH 4 of 6] Adjusted mmap() type casts

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748209484 -10800
#  Mon May 26 00:44:44 2025 +0300
# Node ID 3e11162ed39312387fc79a731071b0a50aa15acc
# Parent  4eed7851024b4ca5d77107676025e84324e23c74
Adjusted mmap() type casts.

Type casting mmap() result explicitly to (u_char *) is only needed on
systems where mmap() returns caddr_t, but on these systems MAP_FAILED
also needs to be casted.  Added appropriate type casts.

diff --git a/src/os/unix/ngx_shmem.c b/src/os/unix/ngx_shmem.c
--- a/src/os/unix/ngx_shmem.c
+++ b/src/os/unix/ngx_shmem.c
@@ -18,7 +18,7 @@ ngx_shm_alloc(ngx_shm_t *shm)
 PROT_READ|PROT_WRITE,
 MAP_ANON|MAP_SHARED, -1, 0);
 
-if (shm->addr == MAP_FAILED) {
+if (shm->addr == (u_char *) MAP_FAILED) {
 ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
   "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
 return NGX_ERROR;
@@ -55,7 +55,7 @@ ngx_shm_alloc(ngx_shm_t *shm)
 shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,
 MAP_SHARED, fd, 0);
 
-if (shm->addr == MAP_FAILED) {
+if (shm->addr == (u_char *) MAP_FAILED) {
 ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
   "mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size);
 }
@@ -65,7 +65,7 @@ ngx_shm_alloc(ngx_shm_t *shm)
   "close(\"/dev/zero\") failed");
 }
 
-return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;
+return (shm->addr == (u_char *) MAP_FAILED) ? NGX_ERROR : NGX_OK;
 }
 
 



[PATCH 0 of 6] various compatibility fixes

2025-05-25 Thread Maxim Dounin
Hello!

The following patch series fixes some compilation issues as observed
on various exotic platforms, notably seen on MINIX, Cygwin, and
FreeBSD 2.2.9.

-- 
Maxim Dounin



[PATCH 2 of 6] Configure: added uint8_t and uint16_t detection

2025-05-25 Thread Maxim Dounin
# HG changeset patch
# User Maxim Dounin 
# Date 1748209464 -10800
#  Mon May 26 00:44:24 2025 +0300
# Node ID 38f1f374523ff2630e59f6a722fd6f9d7f70ac01
# Parent  03f5c84e7e8c05ce16f6f36452e4cb818c85dde6
Configure: added uint8_t and uint16_t detection.

This fixes compilation on some old systems (though mostly for consistency
with uint32_t and uint64_t).

diff --git a/auto/unix b/auto/unix
--- a/auto/unix
+++ b/auto/unix
@@ -645,6 +645,8 @@ ngx_param=NGX_PTR_SIZE; ngx_value=$ngx_s
 
 NGX_INCLUDE_AUTO_CONFIG_H="#include \"ngx_auto_config.h\""
 
+ngx_type="uint8_t"; ngx_types="u_int8_t"; . auto/types/typedef
+ngx_type="uint16_t"; ngx_types="u_int16_t"; . auto/types/typedef
 ngx_type="uint32_t"; ngx_types="u_int32_t"; . auto/types/typedef
 ngx_type="uint64_t"; ngx_types="u_int64_t"; . auto/types/typedef