src/os/unix/ngx_freebsd_sendfile_chain.c - nginx source code

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_err_t        err;
  33.     ngx_buf_t       *file;
  34.     ngx_uint_t       eintr, eagain;
  35. #if (NGX_HAVE_SENDFILE_NODISKIO)
  36.     ngx_uint_t       ebusy;
  37. #endif
  38.     ngx_event_t     *wev;
  39.     ngx_chain_t     *cl;
  40.     ngx_iovec_t      header, trailer;
  41.     struct sf_hdtr   hdtr;
  42.     struct iovec     headers[NGX_IOVS_PREALLOCATE];
  43.     struct iovec     trailers[NGX_IOVS_PREALLOCATE];

  44.     wev = c->write;

  45.     if (!wev->ready) {
  46.         return in;
  47.     }

  48. #if (NGX_HAVE_KQUEUE)

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

  55. #endif

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

  57.     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
  58.         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
  59.     }

  60.     send = 0;
  61.     eagain = 0;
  62.     flags = 0;

  63.     header.iovs = headers;
  64.     header.nalloc = NGX_IOVS_PREALLOCATE;

  65.     trailer.iovs = trailers;
  66.     trailer.nalloc = NGX_IOVS_PREALLOCATE;

  67.     for ( ;; ) {
  68.         eintr = 0;
  69. #if (NGX_HAVE_SENDFILE_NODISKIO)
  70.         ebusy = 0;
  71. #endif
  72.         prev_send = send;

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

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

  75.         if (cl == NGX_CHAIN_ERROR) {
  76.             return NGX_CHAIN_ERROR;
  77.         }

  78.         send += header.size;

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

  81.             /* coalesce the neighbouring file bufs */

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

  83.             send += file_size;

  84.             if (send < limit) {

  85.                 /*
  86.                  * create the trailer iovec and coalesce the neighbouring bufs
  87.                  */

  88.                 cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,
  89.                                                c->log);
  90.                 if (cl == NGX_CHAIN_ERROR) {
  91.                     return NGX_CHAIN_ERROR;
  92.                 }

  93.                 send += trailer.size;

  94.             } else {
  95.                 trailer.count = 0;
  96.             }

  97.             if (ngx_freebsd_use_tcp_nopush
  98.                 && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)
  99.             {
  100.                 if (ngx_tcp_nopush(c->fd) == -1) {
  101.                     err = ngx_socket_errno;

  102.                     /*
  103.                      * there is a tiny chance to be interrupted, however,
  104.                      * we continue a processing without the TCP_NOPUSH
  105.                      */

  106.                     if (err != NGX_EINTR) {
  107.                         wev->error = 1;
  108.                         (void) ngx_connection_error(c, err,
  109.                                                     ngx_tcp_nopush_n " failed");
  110.                         return NGX_CHAIN_ERROR;
  111.                     }

  112.                 } else {
  113.                     c->tcp_nopush = NGX_TCP_NOPUSH_SET;

  114.                     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
  115.                                    "tcp_nopush");
  116.                 }
  117.             }

  118.             /*
  119.              * sendfile() does unneeded work if sf_hdtr's count is 0,
  120.              * but corresponding pointer is not NULL
  121.              */

  122.             hdtr.headers = header.count ? header.iovs : NULL;
  123.             hdtr.hdr_cnt = header.count;
  124.             hdtr.trailers = trailer.count ? trailer.iovs : NULL;
  125.             hdtr.trl_cnt = trailer.count;

  126.             /*
  127.              * the "nbytes bug" of the old sendfile() syscall:
  128.              * http://bugs.freebsd.org/33771
  129.              */

  130.             if (!ngx_freebsd_sendfile_nbytes_bug) {
  131.                 header.size = 0;
  132.             }

  133.             sent = 0;

  134. #if (NGX_HAVE_SENDFILE_NODISKIO)

  135.             flags = (c->busy_count <= 2) ? SF_NODISKIO : 0;

  136.             if (file->file->directio) {
  137.                 flags |= SF_NOCACHE;
  138.             }

  139. #endif

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

  142.             if (rc == -1) {
  143.                 err = ngx_errno;

  144.                 switch (err) {
  145.                 case NGX_EAGAIN:
  146.                     eagain = 1;
  147.                     break;

  148.                 case NGX_EINTR:
  149.                     eintr = 1;
  150.                     break;

  151. #if (NGX_HAVE_SENDFILE_NODISKIO)
  152.                 case NGX_EBUSY:
  153.                     ebusy = 1;
  154.                     break;
  155. #endif

  156.                 default:
  157.                     wev->error = 1;
  158.                     (void) ngx_connection_error(c, err, "sendfile() failed");
  159.                     return NGX_CHAIN_ERROR;
  160.                 }

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

  163.             /*
  164.              * sendfile() in FreeBSD 3.x-4.x may return value >= 0
  165.              * on success, although only 0 is documented
  166.              */

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

  168.                 /*
  169.                  * if rc is OK and sent equal to zero, then someone
  170.                  * has truncated the file, so the offset became beyond
  171.                  * the end of the file
  172.                  */

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

  176.                 return NGX_CHAIN_ERROR;
  177.             }

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

  181.         } else {
  182.             n = ngx_writev(c, &header);

  183.             if (n == NGX_ERROR) {
  184.                 return NGX_CHAIN_ERROR;
  185.             }

  186.             sent = (n == NGX_AGAIN) ? 0 : n;
  187.         }

  188.         c->sent += sent;

  189.         in = ngx_chain_update_sent(in, sent);

  190. #if (NGX_HAVE_SENDFILE_NODISKIO)

  191.         if (ebusy) {
  192.             if (sent == 0) {
  193.                 c->busy_count++;

  194.                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  195.                                "sendfile() busy, count:%d", c->busy_count);

  196.             } else {
  197.                 c->busy_count = 0;
  198.             }

  199.             if (wev->posted) {
  200.                 ngx_delete_posted_event(wev);
  201.             }

  202.             ngx_post_event(wev, &ngx_posted_next_events);

  203.             wev->ready = 0;
  204.             return in;
  205.         }

  206.         c->busy_count = 0;

  207. #endif

  208.         if (eagain) {

  209.             /*
  210.              * sendfile() may return EAGAIN, even if it has sent a whole file
  211.              * part, it indicates that the successive sendfile() call would
  212.              * return EAGAIN right away and would not send anything.
  213.              * We use it as a hint.
  214.              */

  215.             wev->ready = 0;
  216.             return in;
  217.         }

  218.         if (eintr) {
  219.             send = prev_send + sent;
  220.             continue;
  221.         }

  222.         if (send - prev_send != sent) {
  223.             wev->ready = 0;
  224.             return in;
  225.         }

  226.         if (send >= limit || in == NULL) {
  227.             return in;
  228.         }
  229.     }
  230. }