src/core/ngx_output_chain.c - nginx-1.7.10

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>


  8. #if 0
  9. #define NGX_SENDFILE_LIMIT  4096
  10. #endif

  11. /*
  12. * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
  13. * to an application memory from a device if parameters are aligned
  14. * to device sector boundary (512 bytes).  They fallback to usual read
  15. * operation if the parameters are not aligned.
  16. * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
  17. * sector boundary, otherwise it returns EINVAL.  The sector size is
  18. * usually 512 bytes, however, on XFS it may be 4096 bytes.
  19. */

  20. #define NGX_NONE            1


  21. static ngx_inline ngx_int_t
  22.     ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
  23. static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
  24.     ngx_chain_t **chain, ngx_chain_t *in);
  25. static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
  26.     off_t bsize);
  27. static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
  28.     off_t bsize);
  29. static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);


  30. ngx_int_t
  31. ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
  32. {
  33.     off_t         bsize;
  34.     ngx_int_t     rc, last;
  35.     ngx_chain_t  *cl, *out, **last_out;

  36.     if (ctx->in == NULL && ctx->busy == NULL
  37. #if (NGX_HAVE_FILE_AIO)
  38.         && !ctx->aio
  39. #endif
  40.        )
  41.     {
  42.         /*
  43.          * the short path for the case when the ctx->in and ctx->busy chains
  44.          * are empty, the incoming chain is empty too or has the single buf
  45.          * that does not require the copy
  46.          */

  47.         if (in == NULL) {
  48.             return ctx->output_filter(ctx->filter_ctx, in);
  49.         }

  50.         if (in->next == NULL
  51. #if (NGX_SENDFILE_LIMIT)
  52.             && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
  53. #endif
  54.             && ngx_output_chain_as_is(ctx, in->buf))
  55.         {
  56.             return ctx->output_filter(ctx->filter_ctx, in);
  57.         }
  58.     }

  59.     /* add the incoming buf to the chain ctx->in */

  60.     if (in) {
  61.         if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
  62.             return NGX_ERROR;
  63.         }
  64.     }

  65.     out = NULL;
  66.     last_out = &out;
  67.     last = NGX_NONE;

  68.     for ( ;; ) {

  69. #if (NGX_HAVE_FILE_AIO)
  70.         if (ctx->aio) {
  71.             return NGX_AGAIN;
  72.         }
  73. #endif

  74.         while (ctx->in) {

  75.             /*
  76.              * cycle while there are the ctx->in bufs
  77.              * and there are the free output bufs to copy in
  78.              */

  79.             bsize = ngx_buf_size(ctx->in->buf);

  80.             if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {

  81.                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
  82.                               "zero size buf in output "
  83.                               "t:%d r:%d f:%d %p %p-%p %p %O-%O",
  84.                               ctx->in->buf->temporary,
  85.                               ctx->in->buf->recycled,
  86.                               ctx->in->buf->in_file,
  87.                               ctx->in->buf->start,
  88.                               ctx->in->buf->pos,
  89.                               ctx->in->buf->last,
  90.                               ctx->in->buf->file,
  91.                               ctx->in->buf->file_pos,
  92.                               ctx->in->buf->file_last);

  93.                 ngx_debug_point();

  94.                 ctx->in = ctx->in->next;

  95.                 continue;
  96.             }

  97.             if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {

  98.                 /* move the chain link to the output chain */

  99.                 cl = ctx->in;
  100.                 ctx->in = cl->next;

  101.                 *last_out = cl;
  102.                 last_out = &cl->next;
  103.                 cl->next = NULL;

  104.                 continue;
  105.             }

  106.             if (ctx->buf == NULL) {

  107.                 rc = ngx_output_chain_align_file_buf(ctx, bsize);

  108.                 if (rc == NGX_ERROR) {
  109.                     return NGX_ERROR;
  110.                 }

  111.                 if (rc != NGX_OK) {

  112.                     if (ctx->free) {

  113.                         /* get the free buf */

  114.                         cl = ctx->free;
  115.                         ctx->buf = cl->buf;
  116.                         ctx->free = cl->next;

  117.                         ngx_free_chain(ctx->pool, cl);

  118.                     } else if (out || ctx->allocated == ctx->bufs.num) {

  119.                         break;

  120.                     } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
  121.                         return NGX_ERROR;
  122.                     }
  123.                 }
  124.             }

  125.             rc = ngx_output_chain_copy_buf(ctx);

  126.             if (rc == NGX_ERROR) {
  127.                 return rc;
  128.             }

  129.             if (rc == NGX_AGAIN) {
  130.                 if (out) {
  131.                     break;
  132.                 }

  133.                 return rc;
  134.             }

  135.             /* delete the completed buf from the ctx->in chain */

  136.             if (ngx_buf_size(ctx->in->buf) == 0) {
  137.                 ctx->in = ctx->in->next;
  138.             }

  139.             cl = ngx_alloc_chain_link(ctx->pool);
  140.             if (cl == NULL) {
  141.                 return NGX_ERROR;
  142.             }

  143.             cl->buf = ctx->buf;
  144.             cl->next = NULL;
  145.             *last_out = cl;
  146.             last_out = &cl->next;
  147.             ctx->buf = NULL;
  148.         }

  149.         if (out == NULL && last != NGX_NONE) {

  150.             if (ctx->in) {
  151.                 return NGX_AGAIN;
  152.             }

  153.             return last;
  154.         }

  155.         last = ctx->output_filter(ctx->filter_ctx, out);

  156.         if (last == NGX_ERROR || last == NGX_DONE) {
  157.             return last;
  158.         }

  159.         ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
  160.                                 ctx->tag);
  161.         last_out = &out;
  162.     }
  163. }


  164. static ngx_inline ngx_int_t
  165. ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
  166. {
  167.     ngx_uint_t  sendfile;

  168.     if (ngx_buf_special(buf)) {
  169.         return 1;
  170.     }

  171.     if (buf->in_file && buf->file->directio) {
  172.         return 0;
  173.     }

  174.     sendfile = ctx->sendfile;

  175. #if (NGX_SENDFILE_LIMIT)

  176.     if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
  177.         sendfile = 0;
  178.     }

  179. #endif

  180.     if (!sendfile) {

  181.         if (!ngx_buf_in_memory(buf)) {
  182.             return 0;
  183.         }

  184.         buf->in_file = 0;
  185.     }

  186.     if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
  187.         return 0;
  188.     }

  189.     if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
  190.         return 0;
  191.     }

  192.     return 1;
  193. }


  194. static ngx_int_t
  195. ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
  196.     ngx_chain_t *in)
  197. {
  198.     ngx_chain_t  *cl, **ll;
  199. #if (NGX_SENDFILE_LIMIT)
  200.     ngx_buf_t    *b, *buf;
  201. #endif

  202.     ll = chain;

  203.     for (cl = *chain; cl; cl = cl->next) {
  204.         ll = &cl->next;
  205.     }

  206.     while (in) {

  207.         cl = ngx_alloc_chain_link(pool);
  208.         if (cl == NULL) {
  209.             return NGX_ERROR;
  210.         }

  211. #if (NGX_SENDFILE_LIMIT)

  212.         buf = in->buf;

  213.         if (buf->in_file
  214.             && buf->file_pos < NGX_SENDFILE_LIMIT
  215.             && buf->file_last > NGX_SENDFILE_LIMIT)
  216.         {
  217.             /* split a file buf on two bufs by the sendfile limit */

  218.             b = ngx_calloc_buf(pool);
  219.             if (b == NULL) {
  220.                 return NGX_ERROR;
  221.             }

  222.             ngx_memcpy(b, buf, sizeof(ngx_buf_t));

  223.             if (ngx_buf_in_memory(buf)) {
  224.                 buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);
  225.                 b->last = buf->pos;
  226.             }

  227.             buf->file_pos = NGX_SENDFILE_LIMIT;
  228.             b->file_last = NGX_SENDFILE_LIMIT;

  229.             cl->buf = b;

  230.         } else {
  231.             cl->buf = buf;
  232.             in = in->next;
  233.         }

  234. #else
  235.         cl->buf = in->buf;
  236.         in = in->next;

  237. #endif

  238.         cl->next = NULL;
  239.         *ll = cl;
  240.         ll = &cl->next;
  241.     }

  242.     return NGX_OK;
  243. }


  244. static ngx_int_t
  245. ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
  246. {
  247.     size_t      size;
  248.     ngx_buf_t  *in;

  249.     in = ctx->in->buf;

  250.     if (in->file == NULL || !in->file->directio) {
  251.         return NGX_DECLINED;
  252.     }

  253.     ctx->directio = 1;

  254.     size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));

  255.     if (size == 0) {

  256.         if (bsize >= (off_t) ctx->bufs.size) {
  257.             return NGX_DECLINED;
  258.         }

  259.         size = (size_t) bsize;

  260.     } else {
  261.         size = (size_t) ctx->alignment - size;

  262.         if ((off_t) size > bsize) {
  263.             size = (size_t) bsize;
  264.         }
  265.     }

  266.     ctx->buf = ngx_create_temp_buf(ctx->pool, size);
  267.     if (ctx->buf == NULL) {
  268.         return NGX_ERROR;
  269.     }

  270.     /*
  271.      * we do not set ctx->buf->tag, because we do not want
  272.      * to reuse the buf via ctx->free list
  273.      */

  274. #if (NGX_HAVE_ALIGNED_DIRECTIO)
  275.     ctx->unaligned = 1;
  276. #endif

  277.     return NGX_OK;
  278. }


  279. static ngx_int_t
  280. ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
  281. {
  282.     size_t       size;
  283.     ngx_buf_t   *b, *in;
  284.     ngx_uint_t   recycled;

  285.     in = ctx->in->buf;
  286.     size = ctx->bufs.size;
  287.     recycled = 1;

  288.     if (in->last_in_chain) {

  289.         if (bsize < (off_t) size) {

  290.             /*
  291.              * allocate a small temp buf for a small last buf
  292.              * or its small last part
  293.              */

  294.             size = (size_t) bsize;
  295.             recycled = 0;

  296.         } else if (!ctx->directio
  297.                    && ctx->bufs.num == 1
  298.                    && (bsize < (off_t) (size + size / 4)))
  299.         {
  300.             /*
  301.              * allocate a temp buf that equals to a last buf,
  302.              * if there is no directio, the last buf size is lesser
  303.              * than 1.25 of bufs.size and the temp buf is single
  304.              */

  305.             size = (size_t) bsize;
  306.             recycled = 0;
  307.         }
  308.     }

  309.     b = ngx_calloc_buf(ctx->pool);
  310.     if (b == NULL) {
  311.         return NGX_ERROR;
  312.     }

  313.     if (ctx->directio) {

  314.         /*
  315.          * allocate block aligned to a disk sector size to enable
  316.          * userland buffer direct usage conjunctly with directio
  317.          */

  318.         b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);
  319.         if (b->start == NULL) {
  320.             return NGX_ERROR;
  321.         }

  322.     } else {
  323.         b->start = ngx_palloc(ctx->pool, size);
  324.         if (b->start == NULL) {
  325.             return NGX_ERROR;
  326.         }
  327.     }

  328.     b->pos = b->start;
  329.     b->last = b->start;
  330.     b->end = b->last + size;
  331.     b->temporary = 1;
  332.     b->tag = ctx->tag;
  333.     b->recycled = recycled;

  334.     ctx->buf = b;
  335.     ctx->allocated++;

  336.     return NGX_OK;
  337. }


  338. static ngx_int_t
  339. ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
  340. {
  341.     off_t        size;
  342.     ssize_t      n;
  343.     ngx_buf_t   *src, *dst;
  344.     ngx_uint_t   sendfile;

  345.     src = ctx->in->buf;
  346.     dst = ctx->buf;

  347.     size = ngx_buf_size(src);
  348.     size = ngx_min(size, dst->end - dst->pos);

  349.     sendfile = ctx->sendfile & !ctx->directio;

  350. #if (NGX_SENDFILE_LIMIT)

  351.     if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
  352.         sendfile = 0;
  353.     }

  354. #endif

  355.     if (ngx_buf_in_memory(src)) {
  356.         ngx_memcpy(dst->pos, src->pos, (size_t) size);
  357.         src->pos += (size_t) size;
  358.         dst->last += (size_t) size;

  359.         if (src->in_file) {

  360.             if (sendfile) {
  361.                 dst->in_file = 1;
  362.                 dst->file = src->file;
  363.                 dst->file_pos = src->file_pos;
  364.                 dst->file_last = src->file_pos + size;

  365.             } else {
  366.                 dst->in_file = 0;
  367.             }

  368.             src->file_pos += size;

  369.         } else {
  370.             dst->in_file = 0;
  371.         }

  372.         if (src->pos == src->last) {
  373.             dst->flush = src->flush;
  374.             dst->last_buf = src->last_buf;
  375.             dst->last_in_chain = src->last_in_chain;
  376.         }

  377.     } else {

  378. #if (NGX_HAVE_ALIGNED_DIRECTIO)

  379.         if (ctx->unaligned) {
  380.             if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {
  381.                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
  382.                               ngx_directio_off_n " \"%s\" failed",
  383.                               src->file->name.data);
  384.             }
  385.         }

  386. #endif

  387. #if (NGX_HAVE_FILE_AIO)

  388.         if (ctx->aio_handler) {
  389.             n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,
  390.                                   src->file_pos, ctx->pool);
  391.             if (n == NGX_AGAIN) {
  392.                 ctx->aio_handler(ctx, src->file);
  393.                 return NGX_AGAIN;
  394.             }

  395.         } else {
  396.             n = ngx_read_file(src->file, dst->pos, (size_t) size,
  397.                               src->file_pos);
  398.         }
  399. #else

  400.         n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);

  401. #endif

  402. #if (NGX_HAVE_ALIGNED_DIRECTIO)

  403.         if (ctx->unaligned) {
  404.             ngx_err_t  err;

  405.             err = ngx_errno;

  406.             if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {
  407.                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
  408.                               ngx_directio_on_n " \"%s\" failed",
  409.                               src->file->name.data);
  410.             }

  411.             ngx_set_errno(err);

  412.             ctx->unaligned = 0;
  413.         }

  414. #endif

  415.         if (n == NGX_ERROR) {
  416.             return (ngx_int_t) n;
  417.         }

  418.         if (n != size) {
  419.             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
  420.                           ngx_read_file_n " read only %z of %O from \"%s\"",
  421.                           n, size, src->file->name.data);
  422.             return NGX_ERROR;
  423.         }

  424.         dst->last += n;

  425.         if (sendfile) {
  426.             dst->in_file = 1;
  427.             dst->file = src->file;
  428.             dst->file_pos = src->file_pos;
  429.             dst->file_last = src->file_pos + n;

  430.         } else {
  431.             dst->in_file = 0;
  432.         }

  433.         src->file_pos += n;

  434.         if (src->file_pos == src->file_last) {
  435.             dst->flush = src->flush;
  436.             dst->last_buf = src->last_buf;
  437.             dst->last_in_chain = src->last_in_chain;
  438.         }
  439.     }

  440.     return NGX_OK;
  441. }


  442. ngx_int_t
  443. ngx_chain_writer(void *data, ngx_chain_t *in)
  444. {
  445.     ngx_chain_writer_ctx_t *ctx = data;

  446.     off_t              size;
  447.     ngx_chain_t       *cl;
  448.     ngx_connection_t  *c;

  449.     c = ctx->connection;

  450.     for (size = 0; in; in = in->next) {

  451. #if 1
  452.         if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
  453.             ngx_debug_point();
  454.         }
  455. #endif

  456.         size += ngx_buf_size(in->buf);

  457.         ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
  458.                        "chain writer buf fl:%d s:%uO",
  459.                        in->buf->flush, ngx_buf_size(in->buf));

  460.         cl = ngx_alloc_chain_link(ctx->pool);
  461.         if (cl == NULL) {
  462.             return NGX_ERROR;
  463.         }

  464.         cl->buf = in->buf;
  465.         cl->next = NULL;
  466.         *ctx->last = cl;
  467.         ctx->last = &cl->next;
  468.     }

  469.     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  470.                    "chain writer in: %p", ctx->out);

  471.     for (cl = ctx->out; cl; cl = cl->next) {

  472. #if 1
  473.         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
  474.             ngx_debug_point();
  475.         }

  476. #endif

  477.         size += ngx_buf_size(cl->buf);
  478.     }

  479.     if (size == 0 && !c->buffered) {
  480.         return NGX_OK;
  481.     }

  482.     ctx->out = c->send_chain(c, ctx->out, ctx->limit);

  483.     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  484.                    "chain writer out: %p", ctx->out);

  485.     if (ctx->out == NGX_CHAIN_ERROR) {
  486.         return NGX_ERROR;
  487.     }

  488.     if (ctx->out == NULL) {
  489.         ctx->last = &ctx->out;

  490.         if (!c->buffered) {
  491.             return NGX_OK;
  492.         }
  493.     }

  494.     return NGX_AGAIN;
  495. }