Annotation of embedaddon/nginx/src/os/unix/ngx_linux_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: * 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>