Annotation of embedaddon/nginx/src/core/ngx_output_chain.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (C) Igor Sysoev
                      4:  * Copyright (C) Nginx, Inc.
                      5:  */
                      6: 
                      7: 
                      8: #include <ngx_config.h>
                      9: #include <ngx_core.h>
                     10: #include <ngx_event.h>
                     11: 
                     12: 
                     13: #if 0
                     14: #define NGX_SENDFILE_LIMIT  4096
                     15: #endif
                     16: 
                     17: /*
                     18:  * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
                     19:  * to an application memory from a device if parameters are aligned
                     20:  * to device sector boundary (512 bytes).  They fallback to usual read
                     21:  * operation if the parameters are not aligned.
                     22:  * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
                     23:  * sector boundary, otherwise it returns EINVAL.  The sector size is
                     24:  * usually 512 bytes, however, on XFS it may be 4096 bytes.
                     25:  */
                     26: 
                     27: #define NGX_NONE            1
                     28: 
                     29: 
                     30: static ngx_inline ngx_int_t
                     31:     ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
                     32: static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
                     33:     ngx_chain_t **chain, ngx_chain_t *in);
                     34: static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
                     35:     off_t bsize);
                     36: static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
                     37:     off_t bsize);
                     38: static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);
                     39: 
                     40: 
                     41: ngx_int_t
                     42: ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
                     43: {
                     44:     off_t         bsize;
                     45:     ngx_int_t     rc, last;
                     46:     ngx_chain_t  *cl, *out, **last_out;
                     47: 
                     48:     if (ctx->in == NULL && ctx->busy == NULL) {
                     49: 
                     50:         /*
                     51:          * the short path for the case when the ctx->in and ctx->busy chains
                     52:          * are empty, the incoming chain is empty too or has the single buf
                     53:          * that does not require the copy
                     54:          */
                     55: 
                     56:         if (in == NULL) {
                     57:             return ctx->output_filter(ctx->filter_ctx, in);
                     58:         }
                     59: 
                     60:         if (in->next == NULL
                     61: #if (NGX_SENDFILE_LIMIT)
                     62:             && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
                     63: #endif
                     64:             && ngx_output_chain_as_is(ctx, in->buf))
                     65:         {
                     66:             return ctx->output_filter(ctx->filter_ctx, in);
                     67:         }
                     68:     }
                     69: 
                     70:     /* add the incoming buf to the chain ctx->in */
                     71: 
                     72:     if (in) {
                     73:         if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
                     74:             return NGX_ERROR;
                     75:         }
                     76:     }
                     77: 
                     78:     out = NULL;
                     79:     last_out = &out;
                     80:     last = NGX_NONE;
                     81: 
                     82:     for ( ;; ) {
                     83: 
                     84: #if (NGX_HAVE_FILE_AIO)
                     85:         if (ctx->aio) {
                     86:             return NGX_AGAIN;
                     87:         }
                     88: #endif
                     89: 
                     90:         while (ctx->in) {
                     91: 
                     92:             /*
                     93:              * cycle while there are the ctx->in bufs
                     94:              * and there are the free output bufs to copy in
                     95:              */
                     96: 
                     97:             bsize = ngx_buf_size(ctx->in->buf);
                     98: 
                     99:             if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
                    100: 
                    101:                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
                    102:                               "zero size buf in output "
                    103:                               "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                    104:                               ctx->in->buf->temporary,
                    105:                               ctx->in->buf->recycled,
                    106:                               ctx->in->buf->in_file,
                    107:                               ctx->in->buf->start,
                    108:                               ctx->in->buf->pos,
                    109:                               ctx->in->buf->last,
                    110:                               ctx->in->buf->file,
                    111:                               ctx->in->buf->file_pos,
                    112:                               ctx->in->buf->file_last);
                    113: 
                    114:                 ngx_debug_point();
                    115: 
                    116:                 ctx->in = ctx->in->next;
                    117: 
                    118:                 continue;
                    119:             }
                    120: 
                    121:             if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
                    122: 
                    123:                 /* move the chain link to the output chain */
                    124: 
                    125:                 cl = ctx->in;
                    126:                 ctx->in = cl->next;
                    127: 
                    128:                 *last_out = cl;
                    129:                 last_out = &cl->next;
                    130:                 cl->next = NULL;
                    131: 
                    132:                 continue;
                    133:             }
                    134: 
                    135:             if (ctx->buf == NULL) {
                    136: 
                    137:                 rc = ngx_output_chain_align_file_buf(ctx, bsize);
                    138: 
                    139:                 if (rc == NGX_ERROR) {
                    140:                     return NGX_ERROR;
                    141:                 }
                    142: 
                    143:                 if (rc != NGX_OK) {
                    144: 
                    145:                     if (ctx->free) {
                    146: 
                    147:                         /* get the free buf */
                    148: 
                    149:                         cl = ctx->free;
                    150:                         ctx->buf = cl->buf;
                    151:                         ctx->free = cl->next;
                    152: 
                    153:                         ngx_free_chain(ctx->pool, cl);
                    154: 
                    155:                     } else if (out || ctx->allocated == ctx->bufs.num) {
                    156: 
                    157:                         break;
                    158: 
                    159:                     } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
                    160:                         return NGX_ERROR;
                    161:                     }
                    162:                 }
                    163:             }
                    164: 
                    165:             rc = ngx_output_chain_copy_buf(ctx);
                    166: 
                    167:             if (rc == NGX_ERROR) {
                    168:                 return rc;
                    169:             }
                    170: 
                    171:             if (rc == NGX_AGAIN) {
                    172:                 if (out) {
                    173:                     break;
                    174:                 }
                    175: 
                    176:                 return rc;
                    177:             }
                    178: 
                    179:             /* delete the completed buf from the ctx->in chain */
                    180: 
                    181:             if (ngx_buf_size(ctx->in->buf) == 0) {
                    182:                 ctx->in = ctx->in->next;
                    183:             }
                    184: 
                    185:             cl = ngx_alloc_chain_link(ctx->pool);
                    186:             if (cl == NULL) {
                    187:                 return NGX_ERROR;
                    188:             }
                    189: 
                    190:             cl->buf = ctx->buf;
                    191:             cl->next = NULL;
                    192:             *last_out = cl;
                    193:             last_out = &cl->next;
                    194:             ctx->buf = NULL;
                    195:         }
                    196: 
                    197:         if (out == NULL && last != NGX_NONE) {
                    198: 
                    199:             if (ctx->in) {
                    200:                 return NGX_AGAIN;
                    201:             }
                    202: 
                    203:             return last;
                    204:         }
                    205: 
                    206:         last = ctx->output_filter(ctx->filter_ctx, out);
                    207: 
                    208:         if (last == NGX_ERROR || last == NGX_DONE) {
                    209:             return last;
                    210:         }
                    211: 
                    212:         ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
                    213:                                 ctx->tag);
                    214:         last_out = &out;
                    215:     }
                    216: }
                    217: 
                    218: 
                    219: static ngx_inline ngx_int_t
                    220: ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
                    221: {
                    222:     ngx_uint_t  sendfile;
                    223: 
                    224:     if (ngx_buf_special(buf)) {
                    225:         return 1;
                    226:     }
                    227: 
                    228:     if (buf->in_file && buf->file->directio) {
                    229:         return 0;
                    230:     }
                    231: 
                    232:     sendfile = ctx->sendfile;
                    233: 
                    234: #if (NGX_SENDFILE_LIMIT)
                    235: 
                    236:     if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
                    237:         sendfile = 0;
                    238:     }
                    239: 
                    240: #endif
                    241: 
                    242:     if (!sendfile) {
                    243: 
                    244:         if (!ngx_buf_in_memory(buf)) {
                    245:             return 0;
                    246:         }
                    247: 
                    248:         buf->in_file = 0;
                    249:     }
                    250: 
                    251:     if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
                    252:         return 0;
                    253:     }
                    254: 
                    255:     if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
                    256:         return 0;
                    257:     }
                    258: 
                    259:     return 1;
                    260: }
                    261: 
                    262: 
                    263: static ngx_int_t
                    264: ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
                    265:     ngx_chain_t *in)
                    266: {
                    267:     ngx_chain_t  *cl, **ll;
                    268: #if (NGX_SENDFILE_LIMIT)
                    269:     ngx_buf_t    *b, *buf;
                    270: #endif
                    271: 
                    272:     ll = chain;
                    273: 
                    274:     for (cl = *chain; cl; cl = cl->next) {
                    275:         ll = &cl->next;
                    276:     }
                    277: 
                    278:     while (in) {
                    279: 
                    280:         cl = ngx_alloc_chain_link(pool);
                    281:         if (cl == NULL) {
                    282:             return NGX_ERROR;
                    283:         }
                    284: 
                    285: #if (NGX_SENDFILE_LIMIT)
                    286: 
                    287:         buf = in->buf;
                    288: 
                    289:         if (buf->in_file
                    290:             && buf->file_pos < NGX_SENDFILE_LIMIT
                    291:             && buf->file_last > NGX_SENDFILE_LIMIT)
                    292:         {
                    293:             /* split a file buf on two bufs by the sendfile limit */
                    294: 
                    295:             b = ngx_calloc_buf(pool);
                    296:             if (b == NULL) {
                    297:                 return NGX_ERROR;
                    298:             }
                    299: 
                    300:             ngx_memcpy(b, buf, sizeof(ngx_buf_t));
                    301: 
                    302:             if (ngx_buf_in_memory(buf)) {
                    303:                 buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);
                    304:                 b->last = buf->pos;
                    305:             }
                    306: 
                    307:             buf->file_pos = NGX_SENDFILE_LIMIT;
                    308:             b->file_last = NGX_SENDFILE_LIMIT;
                    309: 
                    310:             cl->buf = b;
                    311: 
                    312:         } else {
                    313:             cl->buf = buf;
                    314:             in = in->next;
                    315:         }
                    316: 
                    317: #else
                    318:         cl->buf = in->buf;
                    319:         in = in->next;
                    320: 
                    321: #endif
                    322: 
                    323:         cl->next = NULL;
                    324:         *ll = cl;
                    325:         ll = &cl->next;
                    326:     }
                    327: 
                    328:     return NGX_OK;
                    329: }
                    330: 
                    331: 
                    332: static ngx_int_t
                    333: ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
                    334: {
                    335:     size_t      size;
                    336:     ngx_buf_t  *in;
                    337: 
                    338:     in = ctx->in->buf;
                    339: 
                    340:     if (in->file == NULL || !in->file->directio) {
                    341:         return NGX_DECLINED;
                    342:     }
                    343: 
                    344:     ctx->directio = 1;
                    345: 
                    346:     size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));
                    347: 
                    348:     if (size == 0) {
                    349: 
                    350:         if (bsize >= (off_t) ctx->bufs.size) {
                    351:             return NGX_DECLINED;
                    352:         }
                    353: 
                    354:         size = (size_t) bsize;
                    355: 
                    356:     } else {
                    357:         size = (size_t) ctx->alignment - size;
                    358: 
                    359:         if ((off_t) size > bsize) {
                    360:             size = (size_t) bsize;
                    361:         }
                    362:     }
                    363: 
                    364:     ctx->buf = ngx_create_temp_buf(ctx->pool, size);
                    365:     if (ctx->buf == NULL) {
                    366:         return NGX_ERROR;
                    367:     }
                    368: 
                    369:     /*
                    370:      * we do not set ctx->buf->tag, because we do not want
                    371:      * to reuse the buf via ctx->free list
                    372:      */
                    373: 
                    374: #if (NGX_HAVE_ALIGNED_DIRECTIO)
                    375:     ctx->unaligned = 1;
                    376: #endif
                    377: 
                    378:     return NGX_OK;
                    379: }
                    380: 
                    381: 
                    382: static ngx_int_t
                    383: ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
                    384: {
                    385:     size_t       size;
                    386:     ngx_buf_t   *b, *in;
                    387:     ngx_uint_t   recycled;
                    388: 
                    389:     in = ctx->in->buf;
                    390:     size = ctx->bufs.size;
                    391:     recycled = 1;
                    392: 
                    393:     if (in->last_in_chain) {
                    394: 
                    395:         if (bsize < (off_t) size) {
                    396: 
                    397:             /*
                    398:              * allocate a small temp buf for a small last buf
                    399:              * or its small last part
                    400:              */
                    401: 
                    402:             size = (size_t) bsize;
                    403:             recycled = 0;
                    404: 
                    405:         } else if (!ctx->directio
                    406:                    && ctx->bufs.num == 1
                    407:                    && (bsize < (off_t) (size + size / 4)))
                    408:         {
                    409:             /*
                    410:              * allocate a temp buf that equals to a last buf,
                    411:              * if there is no directio, the last buf size is lesser
                    412:              * than 1.25 of bufs.size and the temp buf is single
                    413:              */
                    414: 
                    415:             size = (size_t) bsize;
                    416:             recycled = 0;
                    417:         }
                    418:     }
                    419: 
                    420:     b = ngx_calloc_buf(ctx->pool);
                    421:     if (b == NULL) {
                    422:         return NGX_ERROR;
                    423:     }
                    424: 
                    425:     if (ctx->directio) {
                    426: 
                    427:         /*
                    428:          * allocate block aligned to a disk sector size to enable
                    429:          * userland buffer direct usage conjunctly with directio
                    430:          */
                    431: 
                    432:         b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);
                    433:         if (b->start == NULL) {
                    434:             return NGX_ERROR;
                    435:         }
                    436: 
                    437:     } else {
                    438:         b->start = ngx_palloc(ctx->pool, size);
                    439:         if (b->start == NULL) {
                    440:             return NGX_ERROR;
                    441:         }
                    442:     }
                    443: 
                    444:     b->pos = b->start;
                    445:     b->last = b->start;
                    446:     b->end = b->last + size;
                    447:     b->temporary = 1;
                    448:     b->tag = ctx->tag;
                    449:     b->recycled = recycled;
                    450: 
                    451:     ctx->buf = b;
                    452:     ctx->allocated++;
                    453: 
                    454:     return NGX_OK;
                    455: }
                    456: 
                    457: 
                    458: static ngx_int_t
                    459: ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
                    460: {
                    461:     off_t        size;
                    462:     ssize_t      n;
                    463:     ngx_buf_t   *src, *dst;
                    464:     ngx_uint_t   sendfile;
                    465: 
                    466:     src = ctx->in->buf;
                    467:     dst = ctx->buf;
                    468: 
                    469:     size = ngx_buf_size(src);
                    470:     size = ngx_min(size, dst->end - dst->pos);
                    471: 
                    472:     sendfile = ctx->sendfile & !ctx->directio;
                    473: 
                    474: #if (NGX_SENDFILE_LIMIT)
                    475: 
                    476:     if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
                    477:         sendfile = 0;
                    478:     }
                    479: 
                    480: #endif
                    481: 
                    482:     if (ngx_buf_in_memory(src)) {
                    483:         ngx_memcpy(dst->pos, src->pos, (size_t) size);
                    484:         src->pos += (size_t) size;
                    485:         dst->last += (size_t) size;
                    486: 
                    487:         if (src->in_file) {
                    488: 
                    489:             if (sendfile) {
                    490:                 dst->in_file = 1;
                    491:                 dst->file = src->file;
                    492:                 dst->file_pos = src->file_pos;
                    493:                 dst->file_last = src->file_pos + size;
                    494: 
                    495:             } else {
                    496:                 dst->in_file = 0;
                    497:             }
                    498: 
                    499:             src->file_pos += size;
                    500: 
                    501:         } else {
                    502:             dst->in_file = 0;
                    503:         }
                    504: 
                    505:         if (src->pos == src->last) {
                    506:             dst->flush = src->flush;
                    507:             dst->last_buf = src->last_buf;
                    508:             dst->last_in_chain = src->last_in_chain;
                    509:         }
                    510: 
                    511:     } else {
                    512: 
                    513: #if (NGX_HAVE_ALIGNED_DIRECTIO)
                    514: 
                    515:         if (ctx->unaligned) {
                    516:             if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {
                    517:                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
                    518:                               ngx_directio_off_n " \"%s\" failed",
                    519:                               src->file->name.data);
                    520:             }
                    521:         }
                    522: 
                    523: #endif
                    524: 
                    525: #if (NGX_HAVE_FILE_AIO)
                    526: 
                    527:         if (ctx->aio_handler) {
                    528:             n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,
                    529:                                   src->file_pos, ctx->pool);
                    530:             if (n == NGX_AGAIN) {
                    531:                 ctx->aio_handler(ctx, src->file);
                    532:                 return NGX_AGAIN;
                    533:             }
                    534: 
                    535:         } else {
                    536:             n = ngx_read_file(src->file, dst->pos, (size_t) size,
                    537:                               src->file_pos);
                    538:         }
                    539: #else
                    540: 
                    541:         n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);
                    542: 
                    543: #endif
                    544: 
                    545: #if (NGX_HAVE_ALIGNED_DIRECTIO)
                    546: 
                    547:         if (ctx->unaligned) {
                    548:             ngx_err_t  err;
                    549: 
                    550:             err = ngx_errno;
                    551: 
                    552:             if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {
                    553:                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
                    554:                               ngx_directio_on_n " \"%s\" failed",
                    555:                               src->file->name.data);
                    556:             }
                    557: 
                    558:             ngx_set_errno(err);
                    559: 
                    560:             ctx->unaligned = 0;
                    561:         }
                    562: 
                    563: #endif
                    564: 
                    565:         if (n == NGX_ERROR) {
                    566:             return (ngx_int_t) n;
                    567:         }
                    568: 
                    569:         if (n != size) {
                    570:             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
                    571:                           ngx_read_file_n " read only %z of %O from \"%s\"",
                    572:                           n, size, src->file->name.data);
                    573:             return NGX_ERROR;
                    574:         }
                    575: 
                    576:         dst->last += n;
                    577: 
                    578:         if (sendfile) {
                    579:             dst->in_file = 1;
                    580:             dst->file = src->file;
                    581:             dst->file_pos = src->file_pos;
                    582:             dst->file_last = src->file_pos + n;
                    583: 
                    584:         } else {
                    585:             dst->in_file = 0;
                    586:         }
                    587: 
                    588:         src->file_pos += n;
                    589: 
                    590:         if (src->file_pos == src->file_last) {
                    591:             dst->flush = src->flush;
                    592:             dst->last_buf = src->last_buf;
                    593:             dst->last_in_chain = src->last_in_chain;
                    594:         }
                    595:     }
                    596: 
                    597:     return NGX_OK;
                    598: }
                    599: 
                    600: 
                    601: ngx_int_t
                    602: ngx_chain_writer(void *data, ngx_chain_t *in)
                    603: {
                    604:     ngx_chain_writer_ctx_t *ctx = data;
                    605: 
                    606:     off_t              size;
                    607:     ngx_chain_t       *cl;
                    608:     ngx_connection_t  *c;
                    609: 
                    610:     c = ctx->connection;
                    611: 
                    612:     for (size = 0; in; in = in->next) {
                    613: 
                    614: #if 1
                    615:         if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
                    616:             ngx_debug_point();
                    617:         }
                    618: #endif
                    619: 
                    620:         size += ngx_buf_size(in->buf);
                    621: 
                    622:         ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
                    623:                        "chain writer buf fl:%d s:%uO",
                    624:                        in->buf->flush, ngx_buf_size(in->buf));
                    625: 
                    626:         cl = ngx_alloc_chain_link(ctx->pool);
                    627:         if (cl == NULL) {
                    628:             return NGX_ERROR;
                    629:         }
                    630: 
                    631:         cl->buf = in->buf;
                    632:         cl->next = NULL;
                    633:         *ctx->last = cl;
                    634:         ctx->last = &cl->next;
                    635:     }
                    636: 
                    637:     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                    638:                    "chain writer in: %p", ctx->out);
                    639: 
                    640:     for (cl = ctx->out; cl; cl = cl->next) {
                    641: 
                    642: #if 1
                    643:         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
                    644:             ngx_debug_point();
                    645:         }
                    646: 
                    647: #endif
                    648: 
                    649:         size += ngx_buf_size(cl->buf);
                    650:     }
                    651: 
                    652:     if (size == 0 && !c->buffered) {
                    653:         return NGX_OK;
                    654:     }
                    655: 
                    656:     ctx->out = c->send_chain(c, ctx->out, ctx->limit);
                    657: 
                    658:     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                    659:                    "chain writer out: %p", ctx->out);
                    660: 
                    661:     if (ctx->out == NGX_CHAIN_ERROR) {
                    662:         return NGX_ERROR;
                    663:     }
                    664: 
                    665:     if (ctx->out == NULL) {
                    666:         ctx->last = &ctx->out;
                    667: 
                    668:         if (!c->buffered) {
                    669:             return NGX_OK;
                    670:         }
                    671:     }
                    672: 
                    673:     return NGX_AGAIN;
                    674: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>