Annotation of embedaddon/nginx/src/os/unix/ngx_linux_sendfile_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: /*
                     14:  * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit
                     15:  * offsets only, and the including <sys/sendfile.h> breaks the compiling,
                     16:  * if off_t is 64 bit wide.  So we use own sendfile() definition, where offset
                     17:  * parameter is int32_t, and use sendfile() for the file parts below 2G only,
                     18:  * see src/os/unix/ngx_linux_config.h
                     19:  *
                     20:  * Linux 2.4.21 has the new sendfile64() syscall #239.
                     21:  *
                     22:  * On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter
                     23:  * more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL,
                     24:  * so we limit it to 2G-1 bytes.
                     25:  */
                     26: 
                     27: #define NGX_SENDFILE_LIMIT  2147483647L
                     28: 
                     29: 
                     30: #if (IOV_MAX > 64)
                     31: #define NGX_HEADERS  64
                     32: #else
                     33: #define NGX_HEADERS  IOV_MAX
                     34: #endif
                     35: 
                     36: 
                     37: ngx_chain_t *
                     38: ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
                     39: {
                     40:     int            rc, tcp_nodelay;
                     41:     off_t          size, send, prev_send, aligned, sent, fprev;
                     42:     u_char        *prev;
                     43:     size_t         file_size;
                     44:     ngx_err_t      err;
                     45:     ngx_buf_t     *file;
                     46:     ngx_uint_t     eintr, complete;
                     47:     ngx_array_t    header;
                     48:     ngx_event_t   *wev;
                     49:     ngx_chain_t   *cl;
                     50:     struct iovec  *iov, headers[NGX_HEADERS];
                     51: #if (NGX_HAVE_SENDFILE64)
                     52:     off_t          offset;
                     53: #else
                     54:     int32_t        offset;
                     55: #endif
                     56: 
                     57:     wev = c->write;
                     58: 
                     59:     if (!wev->ready) {
                     60:         return in;
                     61:     }
                     62: 
                     63: 
                     64:     /* the maximum limit size is 2G-1 - the page size */
                     65: 
                     66:     if (limit == 0 || limit > (off_t) (NGX_SENDFILE_LIMIT - ngx_pagesize)) {
                     67:         limit = NGX_SENDFILE_LIMIT - ngx_pagesize;
                     68:     }
                     69: 
                     70: 
                     71:     send = 0;
                     72: 
                     73:     header.elts = headers;
                     74:     header.size = sizeof(struct iovec);
                     75:     header.nalloc = NGX_HEADERS;
                     76:     header.pool = c->pool;
                     77: 
                     78:     for ( ;; ) {
                     79:         file = NULL;
                     80:         file_size = 0;
                     81:         eintr = 0;
                     82:         complete = 0;
                     83:         prev_send = send;
                     84: 
                     85:         header.nelts = 0;
                     86: 
                     87:         prev = NULL;
                     88:         iov = NULL;
                     89: 
                     90:         /* create the iovec and coalesce the neighbouring bufs */
                     91: 
                     92:         for (cl = in; cl && send < limit; cl = cl->next) {
                     93: 
                     94:             if (ngx_buf_special(cl->buf)) {
                     95:                 continue;
                     96:             }
                     97: 
                     98: #if 1
                     99:             if (!ngx_buf_in_memory(cl->buf) && !cl->buf->in_file) {
                    100:                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                    101:                               "zero size buf in sendfile "
                    102:                               "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                    103:                               cl->buf->temporary,
                    104:                               cl->buf->recycled,
                    105:                               cl->buf->in_file,
                    106:                               cl->buf->start,
                    107:                               cl->buf->pos,
                    108:                               cl->buf->last,
                    109:                               cl->buf->file,
                    110:                               cl->buf->file_pos,
                    111:                               cl->buf->file_last);
                    112: 
                    113:                 ngx_debug_point();
                    114: 
                    115:                 return NGX_CHAIN_ERROR;
                    116:             }
                    117: #endif
                    118: 
                    119:             if (!ngx_buf_in_memory_only(cl->buf)) {
                    120:                 break;
                    121:             }
                    122: 
                    123:             size = cl->buf->last - cl->buf->pos;
                    124: 
                    125:             if (send + size > limit) {
                    126:                 size = limit - send;
                    127:             }
                    128: 
                    129:             if (prev == cl->buf->pos) {
                    130:                 iov->iov_len += (size_t) size;
                    131: 
                    132:             } else {
                    133:                 if (header.nelts >= IOV_MAX) {
                    134:                     break;
                    135:                 }
                    136: 
                    137:                 iov = ngx_array_push(&header);
                    138:                 if (iov == NULL) {
                    139:                     return NGX_CHAIN_ERROR;
                    140:                 }
                    141: 
                    142:                 iov->iov_base = (void *) cl->buf->pos;
                    143:                 iov->iov_len = (size_t) size;
                    144:             }
                    145: 
                    146:             prev = cl->buf->pos + (size_t) size;
                    147:             send += size;
                    148:         }
                    149: 
                    150:         /* set TCP_CORK if there is a header before a file */
                    151: 
                    152:         if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
                    153:             && header.nelts != 0
                    154:             && cl
                    155:             && cl->buf->in_file)
                    156:         {
                    157:             /* the TCP_CORK and TCP_NODELAY are mutually exclusive */
                    158: 
                    159:             if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {
                    160: 
                    161:                 tcp_nodelay = 0;
                    162: 
                    163:                 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
                    164:                                (const void *) &tcp_nodelay, sizeof(int)) == -1)
                    165:                 {
                    166:                     err = ngx_errno;
                    167: 
                    168:                     /*
                    169:                      * there is a tiny chance to be interrupted, however,
                    170:                      * we continue a processing with the TCP_NODELAY
                    171:                      * and without the TCP_CORK
                    172:                      */
                    173: 
                    174:                     if (err != NGX_EINTR) {
                    175:                         wev->error = 1;
                    176:                         ngx_connection_error(c, err,
                    177:                                              "setsockopt(TCP_NODELAY) failed");
                    178:                         return NGX_CHAIN_ERROR;
                    179:                     }
                    180: 
                    181:                 } else {
                    182:                     c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;
                    183: 
                    184:                     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    185:                                    "no tcp_nodelay");
                    186:                 }
                    187:             }
                    188: 
                    189:             if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
                    190: 
                    191:                 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
                    192:                     err = ngx_errno;
                    193: 
                    194:                     /*
                    195:                      * there is a tiny chance to be interrupted, however,
                    196:                      * we continue a processing without the TCP_CORK
                    197:                      */
                    198: 
                    199:                     if (err != NGX_EINTR) {
                    200:                         wev->error = 1;
                    201:                         ngx_connection_error(c, err,
                    202:                                              ngx_tcp_nopush_n " failed");
                    203:                         return NGX_CHAIN_ERROR;
                    204:                     }
                    205: 
                    206:                 } else {
                    207:                     c->tcp_nopush = NGX_TCP_NOPUSH_SET;
                    208: 
                    209:                     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    210:                                    "tcp_nopush");
                    211:                 }
                    212:             }
                    213:         }
                    214: 
                    215:         /* get the file buf */
                    216: 
                    217:         if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) {
                    218:             file = cl->buf;
                    219: 
                    220:             /* coalesce the neighbouring file bufs */
                    221: 
                    222:             do {
                    223:                 size = cl->buf->file_last - cl->buf->file_pos;
                    224: 
                    225:                 if (send + size > limit) {
                    226:                     size = limit - send;
                    227: 
                    228:                     aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
                    229:                                & ~((off_t) ngx_pagesize - 1);
                    230: 
                    231:                     if (aligned <= cl->buf->file_last) {
                    232:                         size = aligned - cl->buf->file_pos;
                    233:                     }
                    234:                 }
                    235: 
                    236:                 file_size += (size_t) size;
                    237:                 send += size;
                    238:                 fprev = cl->buf->file_pos + size;
                    239:                 cl = cl->next;
                    240: 
                    241:             } while (cl
                    242:                      && cl->buf->in_file
                    243:                      && send < limit
                    244:                      && file->file->fd == cl->buf->file->fd
                    245:                      && fprev == cl->buf->file_pos);
                    246:         }
                    247: 
                    248:         if (file) {
                    249: #if 1
                    250:             if (file_size == 0) {
                    251:                 ngx_debug_point();
                    252:                 return NGX_CHAIN_ERROR;
                    253:             }
                    254: #endif
                    255: #if (NGX_HAVE_SENDFILE64)
                    256:             offset = file->file_pos;
                    257: #else
                    258:             offset = (int32_t) file->file_pos;
                    259: #endif
                    260: 
                    261:             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    262:                            "sendfile: @%O %uz", file->file_pos, file_size);
                    263: 
                    264:             rc = sendfile(c->fd, file->file->fd, &offset, file_size);
                    265: 
                    266:             if (rc == -1) {
                    267:                 err = ngx_errno;
                    268: 
                    269:                 switch (err) {
                    270:                 case NGX_EAGAIN:
                    271:                     break;
                    272: 
                    273:                 case NGX_EINTR:
                    274:                     eintr = 1;
                    275:                     break;
                    276: 
                    277:                 default:
                    278:                     wev->error = 1;
                    279:                     ngx_connection_error(c, err, "sendfile() failed");
                    280:                     return NGX_CHAIN_ERROR;
                    281:                 }
                    282: 
                    283:                 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                    284:                                "sendfile() is not ready");
                    285:             }
                    286: 
                    287:             sent = rc > 0 ? rc : 0;
                    288: 
                    289:             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    290:                            "sendfile: %d, @%O %O:%uz",
                    291:                            rc, file->file_pos, sent, file_size);
                    292: 
                    293:         } else {
                    294:             rc = writev(c->fd, header.elts, header.nelts);
                    295: 
                    296:             if (rc == -1) {
                    297:                 err = ngx_errno;
                    298: 
                    299:                 switch (err) {
                    300:                 case NGX_EAGAIN:
                    301:                     break;
                    302: 
                    303:                 case NGX_EINTR:
                    304:                     eintr = 1;
                    305:                     break;
                    306: 
                    307:                 default:
                    308:                     wev->error = 1;
                    309:                     ngx_connection_error(c, err, "writev() failed");
                    310:                     return NGX_CHAIN_ERROR;
                    311:                 }
                    312: 
                    313:                 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                    314:                                "writev() not ready");
                    315:             }
                    316: 
                    317:             sent = rc > 0 ? rc : 0;
                    318: 
                    319:             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %O", sent);
                    320:         }
                    321: 
                    322:         if (send - prev_send == sent) {
                    323:             complete = 1;
                    324:         }
                    325: 
                    326:         c->sent += sent;
                    327: 
                    328:         for (cl = in; cl; cl = cl->next) {
                    329: 
                    330:             if (ngx_buf_special(cl->buf)) {
                    331:                 continue;
                    332:             }
                    333: 
                    334:             if (sent == 0) {
                    335:                 break;
                    336:             }
                    337: 
                    338:             size = ngx_buf_size(cl->buf);
                    339: 
                    340:             if (sent >= size) {
                    341:                 sent -= size;
                    342: 
                    343:                 if (ngx_buf_in_memory(cl->buf)) {
                    344:                     cl->buf->pos = cl->buf->last;
                    345:                 }
                    346: 
                    347:                 if (cl->buf->in_file) {
                    348:                     cl->buf->file_pos = cl->buf->file_last;
                    349:                 }
                    350: 
                    351:                 continue;
                    352:             }
                    353: 
                    354:             if (ngx_buf_in_memory(cl->buf)) {
                    355:                 cl->buf->pos += (size_t) sent;
                    356:             }
                    357: 
                    358:             if (cl->buf->in_file) {
                    359:                 cl->buf->file_pos += sent;
                    360:             }
                    361: 
                    362:             break;
                    363:         }
                    364: 
                    365:         if (eintr) {
                    366:             continue;
                    367:         }
                    368: 
                    369:         if (!complete) {
                    370:             wev->ready = 0;
                    371:             return cl;
                    372:         }
                    373: 
                    374:         if (send >= limit || cl == NULL) {
                    375:             return cl;
                    376:         }
                    377: 
                    378:         in = cl;
                    379:     }
                    380: }

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