Annotation of embedaddon/nginx/src/os/unix/ngx_freebsd_sendfile_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: /*
        !            14:  * Although FreeBSD sendfile() allows to pass a header and a trailer,
        !            15:  * it cannot send a header with a part of the file in one packet until
        !            16:  * FreeBSD 5.3.  Besides, over the fast ethernet connection sendfile()
        !            17:  * may send the partially filled packets, i.e. the 8 file pages may be sent
        !            18:  * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
        !            19:  * and then again the 11 full 1460-bytes packets.
        !            20:  *
        !            21:  * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)
        !            22:  * to postpone the sending - it not only sends a header and the first part of
        !            23:  * the file in one packet, but also sends the file pages in the full packets.
        !            24:  *
        !            25:  * But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending
        !            26:  * data that less than MSS, so that data may be sent with 5 second delay.
        !            27:  * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used
        !            28:  * for non-keepalive HTTP connections.
        !            29:  */
        !            30: 
        !            31: 
        !            32: #if (IOV_MAX > 64)
        !            33: #define NGX_HEADERS   64
        !            34: #define NGX_TRAILERS  64
        !            35: #else
        !            36: #define NGX_HEADERS   IOV_MAX
        !            37: #define NGX_TRAILERS  IOV_MAX
        !            38: #endif
        !            39: 
        !            40: 
        !            41: ngx_chain_t *
        !            42: ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
        !            43: {
        !            44:     int              rc, flags;
        !            45:     u_char          *prev;
        !            46:     off_t            size, send, prev_send, aligned, sent, fprev;
        !            47:     size_t           header_size, file_size;
        !            48:     ngx_uint_t       eintr, eagain, complete;
        !            49:     ngx_err_t        err;
        !            50:     ngx_buf_t       *file;
        !            51:     ngx_array_t      header, trailer;
        !            52:     ngx_event_t     *wev;
        !            53:     ngx_chain_t     *cl;
        !            54:     struct sf_hdtr   hdtr;
        !            55:     struct iovec    *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
        !            56: 
        !            57:     wev = c->write;
        !            58: 
        !            59:     if (!wev->ready) {
        !            60:         return in;
        !            61:     }
        !            62: 
        !            63: #if (NGX_HAVE_KQUEUE)
        !            64: 
        !            65:     if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
        !            66:         (void) ngx_connection_error(c, wev->kq_errno,
        !            67:                                "kevent() reported about an closed connection");
        !            68:         wev->error = 1;
        !            69:         return NGX_CHAIN_ERROR;
        !            70:     }
        !            71: 
        !            72: #endif
        !            73: 
        !            74:     /* the maximum limit size is the maximum size_t value - the page size */
        !            75: 
        !            76:     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
        !            77:         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
        !            78:     }
        !            79: 
        !            80:     send = 0;
        !            81:     eagain = 0;
        !            82:     flags = 0;
        !            83: 
        !            84:     header.elts = headers;
        !            85:     header.size = sizeof(struct iovec);
        !            86:     header.nalloc = NGX_HEADERS;
        !            87:     header.pool = c->pool;
        !            88: 
        !            89:     trailer.elts = trailers;
        !            90:     trailer.size = sizeof(struct iovec);
        !            91:     trailer.nalloc = NGX_TRAILERS;
        !            92:     trailer.pool = c->pool;
        !            93: 
        !            94:     for ( ;; ) {
        !            95:         file = NULL;
        !            96:         file_size = 0;
        !            97:         header_size = 0;
        !            98:         eintr = 0;
        !            99:         complete = 0;
        !           100:         prev_send = send;
        !           101: 
        !           102:         header.nelts = 0;
        !           103:         trailer.nelts = 0;
        !           104: 
        !           105:         /* create the header iovec and coalesce the neighbouring bufs */
        !           106: 
        !           107:         prev = NULL;
        !           108:         iov = NULL;
        !           109: 
        !           110:         for (cl = in; cl && send < limit; cl = cl->next) {
        !           111: 
        !           112:             if (ngx_buf_special(cl->buf)) {
        !           113:                 continue;
        !           114:             }
        !           115: 
        !           116:             if (!ngx_buf_in_memory_only(cl->buf)) {
        !           117:                 break;
        !           118:             }
        !           119: 
        !           120:             size = cl->buf->last - cl->buf->pos;
        !           121: 
        !           122:             if (send + size > limit) {
        !           123:                 size = limit - send;
        !           124:             }
        !           125: 
        !           126:             if (prev == cl->buf->pos) {
        !           127:                 iov->iov_len += (size_t) size;
        !           128: 
        !           129:             } else {
        !           130:                 if (header.nelts >= IOV_MAX){
        !           131:                     break;
        !           132:                 }
        !           133: 
        !           134:                 iov = ngx_array_push(&header);
        !           135:                 if (iov == NULL) {
        !           136:                     return NGX_CHAIN_ERROR;
        !           137:                 }
        !           138: 
        !           139:                 iov->iov_base = (void *) cl->buf->pos;
        !           140:                 iov->iov_len = (size_t) size;
        !           141:             }
        !           142: 
        !           143:             prev = cl->buf->pos + (size_t) size;
        !           144:             header_size += (size_t) size;
        !           145:             send += size;
        !           146:         }
        !           147: 
        !           148: 
        !           149:         if (cl && cl->buf->in_file && send < limit) {
        !           150:             file = cl->buf;
        !           151: 
        !           152:             /* coalesce the neighbouring file bufs */
        !           153: 
        !           154:             do {
        !           155:                 size = cl->buf->file_last - cl->buf->file_pos;
        !           156: 
        !           157:                 if (send + size > limit) {
        !           158:                     size = limit - send;
        !           159: 
        !           160:                     aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
        !           161:                                & ~((off_t) ngx_pagesize - 1);
        !           162: 
        !           163:                     if (aligned <= cl->buf->file_last) {
        !           164:                         size = aligned - cl->buf->file_pos;
        !           165:                     }
        !           166:                 }
        !           167: 
        !           168:                 file_size += (size_t) size;
        !           169:                 send += size;
        !           170:                 fprev = cl->buf->file_pos + size;
        !           171:                 cl = cl->next;
        !           172: 
        !           173:             } while (cl
        !           174:                      && cl->buf->in_file
        !           175:                      && send < limit
        !           176:                      && file->file->fd == cl->buf->file->fd
        !           177:                      && fprev == cl->buf->file_pos);
        !           178:         }
        !           179: 
        !           180: 
        !           181:         if (file) {
        !           182: 
        !           183:             /* create the trailer iovec and coalesce the neighbouring bufs */
        !           184: 
        !           185:             prev = NULL;
        !           186:             iov = NULL;
        !           187: 
        !           188:             while (cl && send < limit) {
        !           189: 
        !           190:                 if (ngx_buf_special(cl->buf)) {
        !           191:                     cl = cl->next;
        !           192:                     continue;
        !           193:                 }
        !           194: 
        !           195:                 if (!ngx_buf_in_memory_only(cl->buf)) {
        !           196:                     break;
        !           197:                 }
        !           198: 
        !           199:                 size = cl->buf->last - cl->buf->pos;
        !           200: 
        !           201:                 if (send + size > limit) {
        !           202:                     size = limit - send;
        !           203:                 }
        !           204: 
        !           205:                 if (prev == cl->buf->pos) {
        !           206:                     iov->iov_len += (size_t) size;
        !           207: 
        !           208:                 } else {
        !           209:                     if (trailer.nelts >= IOV_MAX){
        !           210:                         break;
        !           211:                     }
        !           212: 
        !           213:                     iov = ngx_array_push(&trailer);
        !           214:                     if (iov == NULL) {
        !           215:                         return NGX_CHAIN_ERROR;
        !           216:                     }
        !           217: 
        !           218:                     iov->iov_base = (void *) cl->buf->pos;
        !           219:                     iov->iov_len = (size_t) size;
        !           220:                 }
        !           221: 
        !           222:                 prev = cl->buf->pos + (size_t) size;
        !           223:                 send += size;
        !           224:                 cl = cl->next;
        !           225:             }
        !           226:         }
        !           227: 
        !           228:         if (file) {
        !           229: 
        !           230:             if (ngx_freebsd_use_tcp_nopush
        !           231:                 && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)
        !           232:             {
        !           233:                 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
        !           234:                     err = ngx_errno;
        !           235: 
        !           236:                     /*
        !           237:                      * there is a tiny chance to be interrupted, however,
        !           238:                      * we continue a processing without the TCP_NOPUSH
        !           239:                      */
        !           240: 
        !           241:                     if (err != NGX_EINTR) {
        !           242:                         wev->error = 1;
        !           243:                         (void) ngx_connection_error(c, err,
        !           244:                                                     ngx_tcp_nopush_n " failed");
        !           245:                         return NGX_CHAIN_ERROR;
        !           246:                     }
        !           247: 
        !           248:                 } else {
        !           249:                     c->tcp_nopush = NGX_TCP_NOPUSH_SET;
        !           250: 
        !           251:                     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
        !           252:                                    "tcp_nopush");
        !           253:                 }
        !           254:             }
        !           255: 
        !           256:             /*
        !           257:              * sendfile() does unneeded work if sf_hdtr's count is 0,
        !           258:              * but corresponding pointer is not NULL
        !           259:              */
        !           260: 
        !           261:             hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
        !           262:             hdtr.hdr_cnt = header.nelts;
        !           263:             hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
        !           264:             hdtr.trl_cnt = trailer.nelts;
        !           265: 
        !           266:             /*
        !           267:              * the "nbytes bug" of the old sendfile() syscall:
        !           268:              * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
        !           269:              */
        !           270: 
        !           271:             if (!ngx_freebsd_sendfile_nbytes_bug) {
        !           272:                 header_size = 0;
        !           273:             }
        !           274: 
        !           275:             sent = 0;
        !           276: 
        !           277: #if (NGX_HAVE_AIO_SENDFILE)
        !           278:             flags = c->aio_sendfile ? SF_NODISKIO : 0;
        !           279: #endif
        !           280: 
        !           281:             rc = sendfile(file->file->fd, c->fd, file->file_pos,
        !           282:                           file_size + header_size, &hdtr, &sent, flags);
        !           283: 
        !           284:             if (rc == -1) {
        !           285:                 err = ngx_errno;
        !           286: 
        !           287:                 switch (err) {
        !           288:                 case NGX_EAGAIN:
        !           289:                     eagain = 1;
        !           290:                     break;
        !           291: 
        !           292:                 case NGX_EINTR:
        !           293:                     eintr = 1;
        !           294:                     break;
        !           295: 
        !           296: #if (NGX_HAVE_AIO_SENDFILE)
        !           297:                 case NGX_EBUSY:
        !           298:                     c->busy_sendfile = file;
        !           299:                     break;
        !           300: #endif
        !           301: 
        !           302:                 default:
        !           303:                     wev->error = 1;
        !           304:                     (void) ngx_connection_error(c, err, "sendfile() failed");
        !           305:                     return NGX_CHAIN_ERROR;
        !           306:                 }
        !           307: 
        !           308:                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
        !           309:                                "sendfile() sent only %O bytes", sent);
        !           310: 
        !           311:             /*
        !           312:              * sendfile() in FreeBSD 3.x-4.x may return value >= 0
        !           313:              * on success, although only 0 is documented
        !           314:              */
        !           315: 
        !           316:             } else if (rc >= 0 && sent == 0) {
        !           317: 
        !           318:                 /*
        !           319:                  * if rc is OK and sent equal to zero, then someone
        !           320:                  * has truncated the file, so the offset became beyond
        !           321:                  * the end of the file
        !           322:                  */
        !           323: 
        !           324:                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
        !           325:                          "sendfile() reported that \"%s\" was truncated at %O",
        !           326:                          file->file->name.data, file->file_pos);
        !           327: 
        !           328:                 return NGX_CHAIN_ERROR;
        !           329:             }
        !           330: 
        !           331:             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
        !           332:                            "sendfile: %d, @%O %O:%uz",
        !           333:                            rc, file->file_pos, sent, file_size + header_size);
        !           334: 
        !           335:         } else {
        !           336:             rc = writev(c->fd, header.elts, header.nelts);
        !           337: 
        !           338:             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
        !           339:                            "writev: %d of %uz", rc, header_size);
        !           340: 
        !           341:             if (rc == -1) {
        !           342:                 err = ngx_errno;
        !           343: 
        !           344:                 switch (err) {
        !           345:                 case NGX_EAGAIN:
        !           346:                     break;
        !           347: 
        !           348:                 case NGX_EINTR:
        !           349:                     eintr = 1;
        !           350:                     break;
        !           351: 
        !           352:                 default:
        !           353:                     wev->error = 1;
        !           354:                     ngx_connection_error(c, err, "writev() failed");
        !           355:                     return NGX_CHAIN_ERROR;
        !           356:                 }
        !           357: 
        !           358:                 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
        !           359:                                "writev() not ready");
        !           360:             }
        !           361: 
        !           362:             sent = rc > 0 ? rc : 0;
        !           363:         }
        !           364: 
        !           365:         if (send - prev_send == sent) {
        !           366:             complete = 1;
        !           367:         }
        !           368: 
        !           369:         c->sent += sent;
        !           370: 
        !           371:         for (cl = in; cl; cl = cl->next) {
        !           372: 
        !           373:             if (ngx_buf_special(cl->buf)) {
        !           374:                 continue;
        !           375:             }
        !           376: 
        !           377:             if (sent == 0) {
        !           378:                 break;
        !           379:             }
        !           380: 
        !           381:             size = ngx_buf_size(cl->buf);
        !           382: 
        !           383:             if (sent >= size) {
        !           384:                 sent -= size;
        !           385: 
        !           386:                 if (ngx_buf_in_memory(cl->buf)) {
        !           387:                     cl->buf->pos = cl->buf->last;
        !           388:                 }
        !           389: 
        !           390:                 if (cl->buf->in_file) {
        !           391:                     cl->buf->file_pos = cl->buf->file_last;
        !           392:                 }
        !           393: 
        !           394:                 continue;
        !           395:             }
        !           396: 
        !           397:             if (ngx_buf_in_memory(cl->buf)) {
        !           398:                 cl->buf->pos += (size_t) sent;
        !           399:             }
        !           400: 
        !           401:             if (cl->buf->in_file) {
        !           402:                 cl->buf->file_pos += sent;
        !           403:             }
        !           404: 
        !           405:             break;
        !           406:         }
        !           407: 
        !           408: #if (NGX_HAVE_AIO_SENDFILE)
        !           409:         if (c->busy_sendfile) {
        !           410:             return cl;
        !           411:         }
        !           412: #endif
        !           413: 
        !           414:         if (eagain) {
        !           415: 
        !           416:             /*
        !           417:              * sendfile() may return EAGAIN, even if it has sent a whole file
        !           418:              * part, it indicates that the successive sendfile() call would
        !           419:              * return EAGAIN right away and would not send anything.
        !           420:              * We use it as a hint.
        !           421:              */
        !           422: 
        !           423:             wev->ready = 0;
        !           424:             return cl;
        !           425:         }
        !           426: 
        !           427:         if (eintr) {
        !           428:             continue;
        !           429:         }
        !           430: 
        !           431:         if (!complete) {
        !           432:             wev->ready = 0;
        !           433:             return cl;
        !           434:         }
        !           435: 
        !           436:         if (send >= limit || cl == NULL) {
        !           437:             return cl;
        !           438:         }
        !           439: 
        !           440:         in = cl;
        !           441:     }
        !           442: }

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