From 738dcbbe7a336f55d29baff1012a5273fee65f19 Mon Sep 17 00:00:00 2001 From: txhno <198242577+txhno@users.noreply.github.com> Date: Wed, 1 Apr 2026 03:57:31 +0530 Subject: [PATCH] Core: directio consistency in open_file_cache and mp4 --- src/core/ngx_open_file_cache.c | 121 +++++++++++++++++++++++-- src/core/ngx_open_file_cache.h | 8 ++ src/core/ngx_output_chain.c | 8 +- src/http/modules/ngx_http_mp4_module.c | 34 ++++--- src/http/ngx_http_file_cache.c | 32 ++++++- 5 files changed, 177 insertions(+), 26 deletions(-) diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c index b23ee78d1..4b513aace 100644 --- 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_open_file_cache_t *cache, 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); @@ -249,6 +252,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, 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 +317,7 @@ ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, } of->is_directio = file->is_directio; + of->is_directio_off = 0; goto update; } @@ -389,6 +394,7 @@ create: file->count = 0; file->use_event = 0; file->event = NULL; + file->directio_off = of->is_directio_off; add_event: @@ -442,6 +448,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; } @@ -920,7 +927,11 @@ ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of, } 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); @@ -1021,7 +1032,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); @@ -1030,12 +1042,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(); @@ -1132,7 +1157,7 @@ ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, 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); @@ -1244,10 +1269,92 @@ ngx_open_file_cache_remove(ngx_event_t *ev) 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; + c->file->directio_off--; + + if (c->file->directio_off > 0) { + return NGX_OK; + } + } + + if (ngx_directio_on(fd) == NGX_FILE_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_open_file_directio_off(ngx_fd_t fd, ngx_pool_t *pool) +{ + ngx_open_file_cache_cleanup_t *c; + + c = ngx_open_file_cache_get_cleanup(pool, fd); + + if (c) { + if (c->directio_off) { + return NGX_OK; + } + + c->directio_off = 1; + c->file->directio_off++; + + if (c->file->directio_off > 1) { + return NGX_OK; + } + } + + if (ngx_directio_off(fd) == NGX_FILE_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_open_file_cache_cleanup_t * +ngx_open_file_cache_get_cleanup(ngx_pool_t *p, ngx_fd_t fd) +{ + ngx_pool_cleanup_t *cln; + ngx_open_file_cache_cleanup_t *c; + + for (cln = p->cleanup; cln; cln = cln->next) { + if (cln->handler == ngx_open_file_cleanup) { + + c = cln->data; + + if (c->file->fd == fd) { + return c; + } + } + } + + return NULL; +} diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h index d119c1296..38c5fb6a2 100644 --- 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; @@ -67,6 +69,8 @@ struct ngx_cached_open_file_s { off_t size; ngx_err_t err; + ngx_uint_t directio_off; + uint32_t uses; #if (NGX_HAVE_OPENAT) @@ -103,6 +107,7 @@ typedef struct { ngx_open_file_cache_t *cache; ngx_cached_open_file_t *file; ngx_uint_t min_uses; + ngx_uint_t directio_off; ngx_log_t *log; } ngx_open_file_cache_cleanup_t; @@ -125,5 +130,8 @@ ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool, ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, ngx_open_file_info_t *of, ngx_pool_t *pool); +ngx_int_t ngx_open_file_directio_on(ngx_fd_t fd, ngx_pool_t *pool); +ngx_int_t ngx_open_file_directio_off(ngx_fd_t fd, ngx_pool_t *pool); + #endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */ diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c index eb467e06a..2cedcac9d 100644 --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -564,7 +564,9 @@ ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx) #if (NGX_HAVE_ALIGNED_DIRECTIO) if (ctx->unaligned) { - if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) { + if (ngx_open_file_directio_off(src->file->fd, ctx->pool) + != NGX_OK) + { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, ngx_directio_off_n " \"%s\" failed", src->file->name.data); @@ -611,7 +613,9 @@ ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx) err = ngx_errno; - if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) { + if (ngx_open_file_directio_on(src->file->fd, ctx->pool) + != NGX_OK) + { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, ngx_directio_on_n " \"%s\" failed", src->file->name.data); diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c index 678d6296c..f3a34a9a1 100644 --- a/src/http/modules/ngx_http_mp4_module.c +++ b/src/http/modules/ngx_http_mp4_module.c @@ -519,7 +519,8 @@ ngx_http_mp4_handler(ngx_http_request_t *r) 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.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; @@ -614,6 +615,21 @@ ngx_http_mp4_handler(ngx_http_request_t *r) if (start >= 0) { r->single_range = 1; + if (of.is_directio && !of.is_directio_off) { + + /* + * DIRECTIO is set on transfer only + * to allow kernel to cache "moov" atom + */ + + if (ngx_open_file_directio_off(of.fd, r->pool) != NGX_OK) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_off_n " \"%s\" failed", path.data); + } + + of.is_directio_off = 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 *r) 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 *r) log->action = "sending mp4 to client"; - if (clcf->directio <= of.size) { + if (of.is_directio_off) { - /* - * DIRECTIO is set on transfer only - * to allow kernel to cache "moov" atom - */ + /* DIRECTIO was switched off, restore it */ - if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) { + if (ngx_open_file_directio_on(of.fd, r->pool) != NGX_OK) { 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; diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c index 5209f003b..aa7d2ce98 100644 --- 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_request_t *r) 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_request_t *r) 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 *r) 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;