3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
13 #define NGX_SENDFILE_LIMIT 4096
17 * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
18 * to an application memory from a device if parameters are aligned
19 * to device sector boundary(512 bytes). They fallback to usual read
20 * operation if the parameters are not aligned.
21 * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
22 * sector boundary, otherwise it returns EINVAL. The sector size is
23 * usually 512 bytes, however, on XFS it may be 4096 bytes.
25 #define NGX_DIRECTIO_BLOCK 4096
31 static ngx_inline ngx_int_t
32 ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
33 static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
34 ngx_chain_t **chain, ngx_chain_t *in);
35 static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
37 static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
39 static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);
43 ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
47 ngx_chain_t *cl, *out, **last_out;
49 if (ctx->in == NULL && ctx->busy == NULL) {
52 * the short path for the case when the ctx->in and ctx->busy chains
53 * are empty, the incoming chain is empty too or has the single buf
54 * that does not require the copy
58 return ctx->output_filter(ctx->filter_ctx, in);
62 #if (NGX_SENDFILE_LIMIT)
63 && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
65 && ngx_output_chain_as_is(ctx, in->buf))
67 return ctx->output_filter(ctx->filter_ctx, in);
71 /* add the incoming buf to the chain ctx->in */
74 if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
88 * cycle while there are the ctx->in bufs
89 * and there are the free output bufs to copy in
92 bsize = ngx_buf_size(ctx->in->buf);
94 if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
96 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
97 "zero size buf in output "
98 "t:%d r:%d f:%d %p %p-%p %p %O-%O",
99 ctx->in->buf->temporary,
100 ctx->in->buf->recycled,
101 ctx->in->buf->in_file,
106 ctx->in->buf->file_pos,
107 ctx->in->buf->file_last);
111 ctx->in = ctx->in->next;
116 if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
118 /* move the chain link to the output chain */
124 last_out = &cl->next;
130 if (ctx->buf == NULL) {
132 rc = ngx_output_chain_align_file_buf(ctx, bsize);
134 if (rc == NGX_ERROR) {
142 /* get the free buf */
146 ctx->free = cl->next;
148 ngx_free_chain(ctx->pool, cl);
150 } else if (out || ctx->allocated == ctx->bufs.num) {
154 } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
160 rc = ngx_output_chain_copy_buf(ctx);
162 if (rc == NGX_ERROR) {
166 if (rc == NGX_AGAIN) {
174 /* delete the completed buf from the ctx->in chain */
176 if (ngx_buf_size(ctx->in->buf) == 0) {
177 ctx->in = ctx->in->next;
180 cl = ngx_alloc_chain_link(ctx->pool);
188 last_out = &cl->next;
192 if (out == NULL && last != NGX_NONE) {
201 last = ctx->output_filter(ctx->filter_ctx, out);
203 if (last == NGX_ERROR || last == NGX_DONE) {
207 ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag);
213 static ngx_inline ngx_int_t
214 ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
218 if (ngx_buf_special(buf)) {
222 if (buf->in_file && buf->file->directio) {
226 sendfile = ctx->sendfile;
228 #if (NGX_SENDFILE_LIMIT)
230 if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
238 if (!ngx_buf_in_memory(buf)) {
245 if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
249 if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
258 ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
261 ngx_chain_t *cl, **ll;
262 #if (NGX_SENDFILE_LIMIT)
268 for (cl = *chain; cl; cl = cl->next) {
274 cl = ngx_alloc_chain_link(pool);
279 #if (NGX_SENDFILE_LIMIT)
284 && buf->file_pos < NGX_SENDFILE_LIMIT
285 && buf->file_last > NGX_SENDFILE_LIMIT)
287 /* split a file buf on two bufs by the sendfile limit */
289 b = ngx_calloc_buf(pool);
294 ngx_memcpy(b, buf, sizeof(ngx_buf_t));
296 if (ngx_buf_in_memory(buf)) {
297 buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);
301 buf->file_pos = NGX_SENDFILE_LIMIT;
302 b->file_last = NGX_SENDFILE_LIMIT;
328 ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
335 if (in->file == NULL || !in->file->directio) {
341 size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1)));
345 if (bsize >= (off_t) ctx->bufs.size) {
349 size = (size_t) bsize;
352 size = NGX_DIRECTIO_BLOCK - size;
354 if ((off_t) size > bsize) {
355 size = (size_t) bsize;
359 ctx->buf = ngx_create_temp_buf(ctx->pool, size);
360 if (ctx->buf == NULL) {
365 * we do not set ctx->buf->tag, because we do not want
366 * to reuse the buf via ctx->free list
369 #if (NGX_HAVE_ALIGNED_DIRECTIO)
378 ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
385 size = ctx->bufs.size;
388 if (in->last_in_chain) {
390 if (bsize < (off_t) size) {
393 * allocate a small temp buf for a small last buf
394 * or its small last part
397 size = (size_t) bsize;
400 } else if (!ctx->directio
401 && ctx->bufs.num == 1
402 && (bsize < (off_t) (size + size / 4)))
405 * allocate a temp buf that equals to a last buf,
406 * if there is no directio, the last buf size is lesser
407 * than 1.25 of bufs.size and the temp buf is single
410 size = (size_t) bsize;
415 b = ngx_calloc_buf(ctx->pool);
423 * allocate block aligned to a disk sector size to enable
424 * userland buffer direct usage conjunctly with directio
427 b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK);
428 if (b->start == NULL) {
433 b->start = ngx_palloc(ctx->pool, size);
434 if (b->start == NULL) {
441 b->end = b->last + size;
444 b->recycled = recycled;
454 ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
458 ngx_buf_t *src, *dst;
464 size = ngx_buf_size(src);
466 if (size > dst->end - dst->pos) {
467 size = dst->end - dst->pos;
470 sendfile = ctx->sendfile & !ctx->directio;
472 #if (NGX_SENDFILE_LIMIT)
474 if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
480 if (ngx_buf_in_memory(src)) {
481 ngx_memcpy(dst->pos, src->pos, (size_t) size);
482 src->pos += (size_t) size;
483 dst->last += (size_t) size;
489 dst->file = src->file;
490 dst->file_pos = src->file_pos;
491 dst->file_last = src->file_pos + size;
497 src->file_pos += size;
503 if (src->pos == src->last) {
504 dst->flush = src->flush;
505 dst->last_buf = src->last_buf;
510 #if (NGX_HAVE_ALIGNED_DIRECTIO)
512 if (ctx->unaligned) {
513 if (ngx_directio_off(src->file->fd) == -1) {
514 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
515 ngx_directio_off_n " \"%s\" failed",
516 src->file->name.data);
522 n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);
524 #if (NGX_HAVE_ALIGNED_DIRECTIO)
526 if (ctx->unaligned) {
531 if (ngx_directio_on(src->file->fd) == -1) {
532 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
533 ngx_directio_on_n " \"%s\" failed",
534 src->file->name.data);
544 if (n == NGX_ERROR) {
545 return (ngx_int_t) n;
548 #if (NGX_FILE_AIO_READ)
549 if (n == NGX_AGAIN) {
550 return (ngx_int_t) n;
555 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
556 ngx_read_file_n " read only %z of %O from \"%s\"",
557 n, size, src->file->name.data);
567 dst->file = src->file;
568 dst->file_pos = src->file_pos;
569 dst->file_last = src->file_pos + n;
577 if (src->file_pos == src->file_last) {
578 dst->flush = src->flush;
579 dst->last_buf = src->last_buf;
588 ngx_chain_writer(void *data, ngx_chain_t *in)
590 ngx_chain_writer_ctx_t *ctx = data;
598 for (size = 0; in; in = in->next) {
601 if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
606 size += ngx_buf_size(in->buf);
608 ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
609 "chain writer buf fl:%d s:%uO",
610 in->buf->flush, ngx_buf_size(in->buf));
612 cl = ngx_alloc_chain_link(ctx->pool);
620 ctx->last = &cl->next;
623 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
624 "chain writer in: %p", ctx->out);
626 for (cl = ctx->out; cl; cl = cl->next) {
629 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
635 size += ngx_buf_size(cl->buf);
638 if (size == 0 && !c->buffered) {
642 ctx->out = c->send_chain(c, ctx->out, ctx->limit);
644 ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
645 "chain writer out: %p", ctx->out);
647 if (ctx->out == NGX_CHAIN_ERROR) {
651 if (ctx->out == NULL) {
652 ctx->last = &ctx->out;