src/os/unix/ngx_freebsd_sendfile_chain.c - nginx-1.7.10

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>


  8. /*
  9. * Although FreeBSD sendfile() allows to pass a header and a trailer,
  10. * it cannot send a header with a part of the file in one packet until
  11. * FreeBSD 5.3.  Besides, over the fast ethernet connection sendfile()
  12. * may send the partially filled packets, i.e. the 8 file pages may be sent
  13. * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
  14. * and then again the 11 full 1460-bytes packets.
  15. *
  16. * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)
  17. * to postpone the sending - it not only sends a header and the first part of
  18. * the file in one packet, but also sends the file pages in the full packets.
  19. *
  20. * But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending
  21. * data that less than MSS, so that data may be sent with 5 second delay.
  22. * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used
  23. * for non-keepalive HTTP connections.
  24. */


  25. ngx_chain_t *
  26. ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
  27. {
  28.     int              rc, flags;
  29.     off_t            send, prev_send, sent;
  30.     size_t           file_size;
  31.     ssize_t          n;
  32.     ngx_uint_t       eintr, eagain;
  33.     ngx_err_t        err;
  34.     ngx_buf_t       *file;
  35.     ngx_event_t     *wev;
  36.     ngx_chain_t     *cl;
  37.     ngx_iovec_t      header, trailer;
  38.     struct sf_hdtr   hdtr;
  39.     struct iovec     headers[NGX_IOVS_PREALLOCATE];
  40.     struct iovec     trailers[NGX_IOVS_PREALLOCATE];

  41.     wev = c->write;

  42.     if (!wev->ready) {
  43.         return in;
  44.     }

  45. #if (NGX_HAVE_KQUEUE)

  46.     if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
  47.         (void) ngx_connection_error(c, wev->kq_errno,
  48.                                "kevent() reported about an closed connection");
  49.         wev->error = 1;
  50.         return NGX_CHAIN_ERROR;
  51.     }

  52. #endif

  53.     /* the maximum limit size is the maximum size_t value - the page size */

  54.     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
  55.         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
  56.     }

  57.     send = 0;
  58.     eagain = 0;
  59.     flags = 0;

  60.     header.iovs = headers;
  61.     header.nalloc = NGX_IOVS_PREALLOCATE;

  62.     trailer.iovs = trailers;
  63.     trailer.nalloc = NGX_IOVS_PREALLOCATE;

  64.     for ( ;; ) {
  65.         eintr = 0;
  66.         prev_send = send;

  67.         /* create the header iovec and coalesce the neighbouring bufs */

  68.         cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);

  69.         if (cl == NGX_CHAIN_ERROR) {
  70.             return NGX_CHAIN_ERROR;
  71.         }

  72.         send += header.size;

  73.         if (cl && cl->buf->in_file && send < limit) {
  74.             file = cl->buf;

  75.             /* coalesce the neighbouring file bufs */

  76.             file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);

  77.             send += file_size;

  78.             /* create the trailer iovec and coalesce the neighbouring bufs */

  79.             cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, c->log);

  80.             if (cl == NGX_CHAIN_ERROR) {
  81.                 return NGX_CHAIN_ERROR;
  82.             }

  83.             send += trailer.size;

  84.             if (ngx_freebsd_use_tcp_nopush
  85.                 && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)
  86.             {
  87.                 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
  88.                     err = ngx_socket_errno;

  89.                     /*
  90.                      * there is a tiny chance to be interrupted, however,
  91.                      * we continue a processing without the TCP_NOPUSH
  92.                      */

  93.                     if (err != NGX_EINTR) {
  94.                         wev->error = 1;
  95.                         (void) ngx_connection_error(c, err,
  96.                                                     ngx_tcp_nopush_n " failed");
  97.                         return NGX_CHAIN_ERROR;
  98.                     }

  99.                 } else {
  100.                     c->tcp_nopush = NGX_TCP_NOPUSH_SET;

  101.                     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
  102.                                    "tcp_nopush");
  103.                 }
  104.             }

  105.             /*
  106.              * sendfile() does unneeded work if sf_hdtr's count is 0,
  107.              * but corresponding pointer is not NULL
  108.              */

  109.             hdtr.headers = header.count ? header.iovs : NULL;
  110.             hdtr.hdr_cnt = header.count;
  111.             hdtr.trailers = trailer.count ? trailer.iovs : NULL;
  112.             hdtr.trl_cnt = trailer.count;

  113.             /*
  114.              * the "nbytes bug" of the old sendfile() syscall:
  115.              * http://bugs.freebsd.org/33771
  116.              */

  117.             if (!ngx_freebsd_sendfile_nbytes_bug) {
  118.                 header.size = 0;
  119.             }

  120.             sent = 0;

  121. #if (NGX_HAVE_AIO_SENDFILE)
  122.             flags = c->aio_sendfile ? SF_NODISKIO : 0;
  123. #endif

  124.             rc = sendfile(file->file->fd, c->fd, file->file_pos,
  125.                           file_size + header.size, &hdtr, &sent, flags);

  126.             if (rc == -1) {
  127.                 err = ngx_errno;

  128.                 switch (err) {
  129.                 case NGX_EAGAIN:
  130.                     eagain = 1;
  131.                     break;

  132.                 case NGX_EINTR:
  133.                     eintr = 1;
  134.                     break;

  135. #if (NGX_HAVE_AIO_SENDFILE)
  136.                 case NGX_EBUSY:
  137.                     c->busy_sendfile = file;
  138.                     break;
  139. #endif

  140.                 default:
  141.                     wev->error = 1;
  142.                     (void) ngx_connection_error(c, err, "sendfile() failed");
  143.                     return NGX_CHAIN_ERROR;
  144.                 }

  145.                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
  146.                                "sendfile() sent only %O bytes", sent);

  147.             /*
  148.              * sendfile() in FreeBSD 3.x-4.x may return value >= 0
  149.              * on success, although only 0 is documented
  150.              */

  151.             } else if (rc >= 0 && sent == 0) {

  152.                 /*
  153.                  * if rc is OK and sent equal to zero, then someone
  154.                  * has truncated the file, so the offset became beyond
  155.                  * the end of the file
  156.                  */

  157.                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  158.                          "sendfile() reported that \"%s\" was truncated at %O",
  159.                          file->file->name.data, file->file_pos);

  160.                 return NGX_CHAIN_ERROR;
  161.             }

  162.             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  163.                            "sendfile: %d, @%O %O:%uz",
  164.                            rc, file->file_pos, sent, file_size + header.size);

  165.         } else {
  166.             n = ngx_writev(c, &header);

  167.             if (n == NGX_ERROR) {
  168.                 return NGX_CHAIN_ERROR;
  169.             }

  170.             sent = (n == NGX_AGAIN) ? 0 : n;
  171.         }

  172.         c->sent += sent;

  173.         in = ngx_chain_update_sent(in, sent);

  174. #if (NGX_HAVE_AIO_SENDFILE)
  175.         if (c->busy_sendfile) {
  176.             return in;
  177.         }
  178. #endif

  179.         if (eagain) {

  180.             /*
  181.              * sendfile() may return EAGAIN, even if it has sent a whole file
  182.              * part, it indicates that the successive sendfile() call would
  183.              * return EAGAIN right away and would not send anything.
  184.              * We use it as a hint.
  185.              */

  186.             wev->ready = 0;
  187.             return in;
  188.         }

  189.         if (eintr) {
  190.             send = prev_send + sent;
  191.             continue;
  192.         }

  193.         if (send - prev_send != sent) {
  194.             wev->ready = 0;
  195.             return in;
  196.         }

  197.         if (send >= limit || in == NULL) {
  198.             return in;
  199.         }
  200.     }
  201. }