Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_event.h>
0011 
0012 
0013 #if 0
0014 #define NGX_SENDFILE_LIMIT  4096
0015 #endif
0016 
0017 /*
0018  * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
0019  * to an application memory from a device if parameters are aligned
0020  * to device sector boundary (512 bytes).  They fallback to usual read
0021  * operation if the parameters are not aligned.
0022  * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
0023  * sector boundary, otherwise it returns EINVAL.  The sector size is
0024  * usually 512 bytes, however, on XFS it may be 4096 bytes.
0025  */
0026 
0027 #define NGX_NONE            1
0028 
0029 
0030 static ngx_inline ngx_int_t
0031     ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
0032 #if (NGX_HAVE_AIO_SENDFILE)
0033 static ngx_int_t ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx,
0034     ngx_file_t *file);
0035 #endif
0036 static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
0037     ngx_chain_t **chain, ngx_chain_t *in);
0038 static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
0039     off_t bsize);
0040 static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
0041     off_t bsize);
0042 static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);
0043 
0044 
0045 ngx_int_t
0046 ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
0047 {
0048     off_t         bsize;
0049     ngx_int_t     rc, last;
0050     ngx_chain_t  *cl, *out, **last_out;
0051 
0052     if (ctx->in == NULL && ctx->busy == NULL
0053 #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
0054         && !ctx->aio
0055 #endif
0056        )
0057     {
0058         /*
0059          * the short path for the case when the ctx->in and ctx->busy chains
0060          * are empty, the incoming chain is empty too or has the single buf
0061          * that does not require the copy
0062          */
0063 
0064         if (in == NULL) {
0065             return ctx->output_filter(ctx->filter_ctx, in);
0066         }
0067 
0068         if (in->next == NULL
0069 #if (NGX_SENDFILE_LIMIT)
0070             && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
0071 #endif
0072             && ngx_output_chain_as_is(ctx, in->buf))
0073         {
0074             return ctx->output_filter(ctx->filter_ctx, in);
0075         }
0076     }
0077 
0078     /* add the incoming buf to the chain ctx->in */
0079 
0080     if (in) {
0081         if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
0082             return NGX_ERROR;
0083         }
0084     }
0085 
0086     out = NULL;
0087     last_out = &out;
0088     last = NGX_NONE;
0089 
0090     for ( ;; ) {
0091 
0092 #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
0093         if (ctx->aio) {
0094             return NGX_AGAIN;
0095         }
0096 #endif
0097 
0098         while (ctx->in) {
0099 
0100             /*
0101              * cycle while there are the ctx->in bufs
0102              * and there are the free output bufs to copy in
0103              */
0104 
0105             bsize = ngx_buf_size(ctx->in->buf);
0106 
0107             if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
0108 
0109                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0110                               "zero size buf in output "
0111                               "t:%d r:%d f:%d %p %p-%p %p %O-%O",
0112                               ctx->in->buf->temporary,
0113                               ctx->in->buf->recycled,
0114                               ctx->in->buf->in_file,
0115                               ctx->in->buf->start,
0116                               ctx->in->buf->pos,
0117                               ctx->in->buf->last,
0118                               ctx->in->buf->file,
0119                               ctx->in->buf->file_pos,
0120                               ctx->in->buf->file_last);
0121 
0122                 ngx_debug_point();
0123 
0124                 ctx->in = ctx->in->next;
0125 
0126                 continue;
0127             }
0128 
0129             if (bsize < 0) {
0130 
0131                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0132                               "negative size buf in output "
0133                               "t:%d r:%d f:%d %p %p-%p %p %O-%O",
0134                               ctx->in->buf->temporary,
0135                               ctx->in->buf->recycled,
0136                               ctx->in->buf->in_file,
0137                               ctx->in->buf->start,
0138                               ctx->in->buf->pos,
0139                               ctx->in->buf->last,
0140                               ctx->in->buf->file,
0141                               ctx->in->buf->file_pos,
0142                               ctx->in->buf->file_last);
0143 
0144                 ngx_debug_point();
0145 
0146                 return NGX_ERROR;
0147             }
0148 
0149             if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
0150 
0151                 /* move the chain link to the output chain */
0152 
0153                 cl = ctx->in;
0154                 ctx->in = cl->next;
0155 
0156                 *last_out = cl;
0157                 last_out = &cl->next;
0158                 cl->next = NULL;
0159 
0160                 continue;
0161             }
0162 
0163             if (ctx->buf == NULL) {
0164 
0165                 rc = ngx_output_chain_align_file_buf(ctx, bsize);
0166 
0167                 if (rc == NGX_ERROR) {
0168                     return NGX_ERROR;
0169                 }
0170 
0171                 if (rc != NGX_OK) {
0172 
0173                     if (ctx->free) {
0174 
0175                         /* get the free buf */
0176 
0177                         cl = ctx->free;
0178                         ctx->buf = cl->buf;
0179                         ctx->free = cl->next;
0180 
0181                         ngx_free_chain(ctx->pool, cl);
0182 
0183                     } else if (out || ctx->allocated == ctx->bufs.num) {
0184 
0185                         break;
0186 
0187                     } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
0188                         return NGX_ERROR;
0189                     }
0190                 }
0191             }
0192 
0193             rc = ngx_output_chain_copy_buf(ctx);
0194 
0195             if (rc == NGX_ERROR) {
0196                 return rc;
0197             }
0198 
0199             if (rc == NGX_AGAIN) {
0200                 if (out) {
0201                     break;
0202                 }
0203 
0204                 return rc;
0205             }
0206 
0207             /* delete the completed buf from the ctx->in chain */
0208 
0209             if (ngx_buf_size(ctx->in->buf) == 0) {
0210                 ctx->in = ctx->in->next;
0211             }
0212 
0213             cl = ngx_alloc_chain_link(ctx->pool);
0214             if (cl == NULL) {
0215                 return NGX_ERROR;
0216             }
0217 
0218             cl->buf = ctx->buf;
0219             cl->next = NULL;
0220             *last_out = cl;
0221             last_out = &cl->next;
0222             ctx->buf = NULL;
0223         }
0224 
0225         if (out == NULL && last != NGX_NONE) {
0226 
0227             if (ctx->in) {
0228                 return NGX_AGAIN;
0229             }
0230 
0231             return last;
0232         }
0233 
0234         last = ctx->output_filter(ctx->filter_ctx, out);
0235 
0236         if (last == NGX_ERROR || last == NGX_DONE) {
0237             return last;
0238         }
0239 
0240         ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
0241                                 ctx->tag);
0242         last_out = &out;
0243     }
0244 }
0245 
0246 
0247 static ngx_inline ngx_int_t
0248 ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
0249 {
0250     ngx_uint_t  sendfile;
0251 
0252     if (ngx_buf_special(buf)) {
0253         return 1;
0254     }
0255 
0256 #if (NGX_THREADS)
0257     if (buf->in_file) {
0258         buf->file->thread_handler = ctx->thread_handler;
0259         buf->file->thread_ctx = ctx->filter_ctx;
0260     }
0261 #endif
0262 
0263     if (buf->in_file && buf->file->directio) {
0264         return 0;
0265     }
0266 
0267     sendfile = ctx->sendfile;
0268 
0269 #if (NGX_SENDFILE_LIMIT)
0270 
0271     if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
0272         sendfile = 0;
0273     }
0274 
0275 #endif
0276 
0277     if (!sendfile) {
0278 
0279         if (!ngx_buf_in_memory(buf)) {
0280             return 0;
0281         }
0282 
0283         buf->in_file = 0;
0284     }
0285 
0286 #if (NGX_HAVE_AIO_SENDFILE)
0287     if (ctx->aio_preload && buf->in_file) {
0288         (void) ngx_output_chain_aio_setup(ctx, buf->file);
0289     }
0290 #endif
0291 
0292     if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
0293         return 0;
0294     }
0295 
0296     if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
0297         return 0;
0298     }
0299 
0300     return 1;
0301 }
0302 
0303 
0304 #if (NGX_HAVE_AIO_SENDFILE)
0305 
0306 static ngx_int_t
0307 ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
0308 {
0309     ngx_event_aio_t  *aio;
0310 
0311     if (file->aio == NULL && ngx_file_aio_init(file, ctx->pool) != NGX_OK) {
0312         return NGX_ERROR;
0313     }
0314 
0315     aio = file->aio;
0316 
0317     aio->data = ctx->filter_ctx;
0318     aio->preload_handler = ctx->aio_preload;
0319 
0320     return NGX_OK;
0321 }
0322 
0323 #endif
0324 
0325 
0326 static ngx_int_t
0327 ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
0328     ngx_chain_t *in)
0329 {
0330     ngx_chain_t  *cl, **ll;
0331 #if (NGX_SENDFILE_LIMIT)
0332     ngx_buf_t    *b, *buf;
0333 #endif
0334 
0335     ll = chain;
0336 
0337     for (cl = *chain; cl; cl = cl->next) {
0338         ll = &cl->next;
0339     }
0340 
0341     while (in) {
0342 
0343         cl = ngx_alloc_chain_link(pool);
0344         if (cl == NULL) {
0345             return NGX_ERROR;
0346         }
0347 
0348 #if (NGX_SENDFILE_LIMIT)
0349 
0350         buf = in->buf;
0351 
0352         if (buf->in_file
0353             && buf->file_pos < NGX_SENDFILE_LIMIT
0354             && buf->file_last > NGX_SENDFILE_LIMIT)
0355         {
0356             /* split a file buf on two bufs by the sendfile limit */
0357 
0358             b = ngx_calloc_buf(pool);
0359             if (b == NULL) {
0360                 return NGX_ERROR;
0361             }
0362 
0363             ngx_memcpy(b, buf, sizeof(ngx_buf_t));
0364 
0365             if (ngx_buf_in_memory(buf)) {
0366                 buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);
0367                 b->last = buf->pos;
0368             }
0369 
0370             buf->file_pos = NGX_SENDFILE_LIMIT;
0371             b->file_last = NGX_SENDFILE_LIMIT;
0372 
0373             cl->buf = b;
0374 
0375         } else {
0376             cl->buf = buf;
0377             in = in->next;
0378         }
0379 
0380 #else
0381         cl->buf = in->buf;
0382         in = in->next;
0383 
0384 #endif
0385 
0386         cl->next = NULL;
0387         *ll = cl;
0388         ll = &cl->next;
0389     }
0390 
0391     return NGX_OK;
0392 }
0393 
0394 
0395 static ngx_int_t
0396 ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
0397 {
0398     size_t      size;
0399     ngx_buf_t  *in;
0400 
0401     in = ctx->in->buf;
0402 
0403     if (in->file == NULL || !in->file->directio) {
0404         return NGX_DECLINED;
0405     }
0406 
0407     ctx->directio = 1;
0408 
0409     size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));
0410 
0411     if (size == 0) {
0412 
0413         if (bsize >= (off_t) ctx->bufs.size) {
0414             return NGX_DECLINED;
0415         }
0416 
0417         size = (size_t) bsize;
0418 
0419     } else {
0420         size = (size_t) ctx->alignment - size;
0421 
0422         if ((off_t) size > bsize) {
0423             size = (size_t) bsize;
0424         }
0425     }
0426 
0427     ctx->buf = ngx_create_temp_buf(ctx->pool, size);
0428     if (ctx->buf == NULL) {
0429         return NGX_ERROR;
0430     }
0431 
0432     /*
0433      * we do not set ctx->buf->tag, because we do not want
0434      * to reuse the buf via ctx->free list
0435      */
0436 
0437 #if (NGX_HAVE_ALIGNED_DIRECTIO)
0438     ctx->unaligned = 1;
0439 #endif
0440 
0441     return NGX_OK;
0442 }
0443 
0444 
0445 static ngx_int_t
0446 ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
0447 {
0448     size_t       size;
0449     ngx_buf_t   *b, *in;
0450     ngx_uint_t   recycled;
0451 
0452     in = ctx->in->buf;
0453     size = ctx->bufs.size;
0454     recycled = 1;
0455 
0456     if (in->last_in_chain) {
0457 
0458         if (bsize < (off_t) size) {
0459 
0460             /*
0461              * allocate a small temp buf for a small last buf
0462              * or its small last part
0463              */
0464 
0465             size = (size_t) bsize;
0466             recycled = 0;
0467 
0468         } else if (!ctx->directio
0469                    && ctx->bufs.num == 1
0470                    && (bsize < (off_t) (size + size / 4)))
0471         {
0472             /*
0473              * allocate a temp buf that equals to a last buf,
0474              * if there is no directio, the last buf size is lesser
0475              * than 1.25 of bufs.size and the temp buf is single
0476              */
0477 
0478             size = (size_t) bsize;
0479             recycled = 0;
0480         }
0481     }
0482 
0483     b = ngx_calloc_buf(ctx->pool);
0484     if (b == NULL) {
0485         return NGX_ERROR;
0486     }
0487 
0488     if (ctx->directio) {
0489 
0490         /*
0491          * allocate block aligned to a disk sector size to enable
0492          * userland buffer direct usage conjunctly with directio
0493          */
0494 
0495         b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);
0496         if (b->start == NULL) {
0497             return NGX_ERROR;
0498         }
0499 
0500     } else {
0501         b->start = ngx_palloc(ctx->pool, size);
0502         if (b->start == NULL) {
0503             return NGX_ERROR;
0504         }
0505     }
0506 
0507     b->pos = b->start;
0508     b->last = b->start;
0509     b->end = b->last + size;
0510     b->temporary = 1;
0511     b->tag = ctx->tag;
0512     b->recycled = recycled;
0513 
0514     ctx->buf = b;
0515     ctx->allocated++;
0516 
0517     return NGX_OK;
0518 }
0519 
0520 
0521 static ngx_int_t
0522 ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
0523 {
0524     off_t        size;
0525     ssize_t      n;
0526     ngx_buf_t   *src, *dst;
0527     ngx_uint_t   sendfile;
0528 
0529     src = ctx->in->buf;
0530     dst = ctx->buf;
0531 
0532     size = ngx_buf_size(src);
0533     size = ngx_min(size, dst->end - dst->pos);
0534 
0535     sendfile = ctx->sendfile && !ctx->directio;
0536 
0537 #if (NGX_SENDFILE_LIMIT)
0538 
0539     if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
0540         sendfile = 0;
0541     }
0542 
0543 #endif
0544 
0545     if (ngx_buf_in_memory(src)) {
0546         ngx_memcpy(dst->pos, src->pos, (size_t) size);
0547         src->pos += (size_t) size;
0548         dst->last += (size_t) size;
0549 
0550         if (src->in_file) {
0551 
0552             if (sendfile) {
0553                 dst->in_file = 1;
0554                 dst->file = src->file;
0555                 dst->file_pos = src->file_pos;
0556                 dst->file_last = src->file_pos + size;
0557 
0558             } else {
0559                 dst->in_file = 0;
0560             }
0561 
0562             src->file_pos += size;
0563 
0564         } else {
0565             dst->in_file = 0;
0566         }
0567 
0568         if (src->pos == src->last) {
0569             dst->flush = src->flush;
0570             dst->last_buf = src->last_buf;
0571             dst->last_in_chain = src->last_in_chain;
0572         }
0573 
0574     } else {
0575 
0576 #if (NGX_HAVE_ALIGNED_DIRECTIO)
0577 
0578         if (ctx->unaligned) {
0579             if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {
0580                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
0581                               ngx_directio_off_n " \"%s\" failed",
0582                               src->file->name.data);
0583             }
0584         }
0585 
0586 #endif
0587 
0588 #if (NGX_HAVE_FILE_AIO)
0589         if (ctx->aio_handler) {
0590             n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,
0591                                   src->file_pos, ctx->pool);
0592             if (n == NGX_AGAIN) {
0593                 ctx->aio_handler(ctx, src->file);
0594                 return NGX_AGAIN;
0595             }
0596 
0597         } else
0598 #endif
0599 #if (NGX_THREADS)
0600         if (ctx->thread_handler) {
0601             src->file->thread_task = ctx->thread_task;
0602             src->file->thread_handler = ctx->thread_handler;
0603             src->file->thread_ctx = ctx->filter_ctx;
0604 
0605             n = ngx_thread_read(src->file, dst->pos, (size_t) size,
0606                                 src->file_pos, ctx->pool);
0607             if (n == NGX_AGAIN) {
0608                 ctx->thread_task = src->file->thread_task;
0609                 return NGX_AGAIN;
0610             }
0611 
0612         } else
0613 #endif
0614         {
0615             n = ngx_read_file(src->file, dst->pos, (size_t) size,
0616                               src->file_pos);
0617         }
0618 
0619 #if (NGX_HAVE_ALIGNED_DIRECTIO)
0620 
0621         if (ctx->unaligned) {
0622             ngx_err_t  err;
0623 
0624             err = ngx_errno;
0625 
0626             if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {
0627                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
0628                               ngx_directio_on_n " \"%s\" failed",
0629                               src->file->name.data);
0630             }
0631 
0632             ngx_set_errno(err);
0633 
0634             ctx->unaligned = 0;
0635         }
0636 
0637 #endif
0638 
0639         if (n == NGX_ERROR) {
0640             return (ngx_int_t) n;
0641         }
0642 
0643         if (n != size) {
0644             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0645                           ngx_read_file_n " read only %z of %O from \"%s\"",
0646                           n, size, src->file->name.data);
0647             return NGX_ERROR;
0648         }
0649 
0650         dst->last += n;
0651 
0652         if (sendfile) {
0653             dst->in_file = 1;
0654             dst->file = src->file;
0655             dst->file_pos = src->file_pos;
0656             dst->file_last = src->file_pos + n;
0657 
0658         } else {
0659             dst->in_file = 0;
0660         }
0661 
0662         src->file_pos += n;
0663 
0664         if (src->file_pos == src->file_last) {
0665             dst->flush = src->flush;
0666             dst->last_buf = src->last_buf;
0667             dst->last_in_chain = src->last_in_chain;
0668         }
0669     }
0670 
0671     return NGX_OK;
0672 }
0673 
0674 
0675 ngx_int_t
0676 ngx_chain_writer(void *data, ngx_chain_t *in)
0677 {
0678     ngx_chain_writer_ctx_t *ctx = data;
0679 
0680     off_t              size;
0681     ngx_chain_t       *cl, *ln, *chain;
0682     ngx_connection_t  *c;
0683 
0684     c = ctx->connection;
0685 
0686     for (size = 0; in; in = in->next) {
0687 
0688         if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
0689 
0690             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0691                           "zero size buf in chain writer "
0692                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
0693                           in->buf->temporary,
0694                           in->buf->recycled,
0695                           in->buf->in_file,
0696                           in->buf->start,
0697                           in->buf->pos,
0698                           in->buf->last,
0699                           in->buf->file,
0700                           in->buf->file_pos,
0701                           in->buf->file_last);
0702 
0703             ngx_debug_point();
0704 
0705             continue;
0706         }
0707 
0708         if (ngx_buf_size(in->buf) < 0) {
0709 
0710             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0711                           "negative size buf in chain writer "
0712                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
0713                           in->buf->temporary,
0714                           in->buf->recycled,
0715                           in->buf->in_file,
0716                           in->buf->start,
0717                           in->buf->pos,
0718                           in->buf->last,
0719                           in->buf->file,
0720                           in->buf->file_pos,
0721                           in->buf->file_last);
0722 
0723             ngx_debug_point();
0724 
0725             return NGX_ERROR;
0726         }
0727 
0728         size += ngx_buf_size(in->buf);
0729 
0730         ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
0731                        "chain writer buf fl:%d s:%uO",
0732                        in->buf->flush, ngx_buf_size(in->buf));
0733 
0734         cl = ngx_alloc_chain_link(ctx->pool);
0735         if (cl == NULL) {
0736             return NGX_ERROR;
0737         }
0738 
0739         cl->buf = in->buf;
0740         cl->next = NULL;
0741         *ctx->last = cl;
0742         ctx->last = &cl->next;
0743     }
0744 
0745     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
0746                    "chain writer in: %p", ctx->out);
0747 
0748     for (cl = ctx->out; cl; cl = cl->next) {
0749 
0750         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
0751 
0752             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0753                           "zero size buf in chain writer "
0754                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
0755                           cl->buf->temporary,
0756                           cl->buf->recycled,
0757                           cl->buf->in_file,
0758                           cl->buf->start,
0759                           cl->buf->pos,
0760                           cl->buf->last,
0761                           cl->buf->file,
0762                           cl->buf->file_pos,
0763                           cl->buf->file_last);
0764 
0765             ngx_debug_point();
0766 
0767             continue;
0768         }
0769 
0770         if (ngx_buf_size(cl->buf) < 0) {
0771 
0772             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
0773                           "negative size buf in chain writer "
0774                           "t:%d r:%d f:%d %p %p-%p %p %O-%O",
0775                           cl->buf->temporary,
0776                           cl->buf->recycled,
0777                           cl->buf->in_file,
0778                           cl->buf->start,
0779                           cl->buf->pos,
0780                           cl->buf->last,
0781                           cl->buf->file,
0782                           cl->buf->file_pos,
0783                           cl->buf->file_last);
0784 
0785             ngx_debug_point();
0786 
0787             return NGX_ERROR;
0788         }
0789 
0790         size += ngx_buf_size(cl->buf);
0791     }
0792 
0793     if (size == 0 && !c->buffered) {
0794         return NGX_OK;
0795     }
0796 
0797     chain = c->send_chain(c, ctx->out, ctx->limit);
0798 
0799     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
0800                    "chain writer out: %p", chain);
0801 
0802     if (chain == NGX_CHAIN_ERROR) {
0803         return NGX_ERROR;
0804     }
0805 
0806     for (cl = ctx->out; cl && cl != chain; /* void */) {
0807         ln = cl;
0808         cl = cl->next;
0809         ngx_free_chain(ctx->pool, ln);
0810     }
0811 
0812     ctx->out = chain;
0813 
0814     if (ctx->out == NULL) {
0815         ctx->last = &ctx->out;
0816 
0817         if (!c->buffered) {
0818             return NGX_OK;
0819         }
0820     }
0821 
0822     return NGX_AGAIN;
0823 }