Annotation of embedaddon/nginx/src/core/ngx_output_chain.c, revision 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>