Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_event.h>
0011 
0012 
0013 static ssize_t ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file,
0014     size_t size);
0015 
0016 #if (NGX_THREADS)
0017 #include <ngx_thread_pool.h>
0018 
0019 #if !(NGX_HAVE_SENDFILE64)
0020 #error sendfile64() is required!
0021 #endif
0022 
0023 static ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file,
0024     size_t size);
0025 static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log);
0026 #endif
0027 
0028 
0029 /*
0030  * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit
0031  * offsets only, and the including <sys/sendfile.h> breaks the compiling,
0032  * if off_t is 64 bit wide.  So we use own sendfile() definition, where offset
0033  * parameter is int32_t, and use sendfile() for the file parts below 2G only,
0034  * see src/os/unix/ngx_linux_config.h
0035  *
0036  * Linux 2.4.21 has the new sendfile64() syscall #239.
0037  *
0038  * On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter
0039  * more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL,
0040  * so we limit it to 2G-1 bytes.
0041  */
0042 
0043 #define NGX_SENDFILE_MAXSIZE  2147483647L
0044 
0045 
0046 ngx_chain_t *
0047 ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
0048 {
0049     int            tcp_nodelay;
0050     off_t          send, prev_send;
0051     size_t         file_size, sent;
0052     ssize_t        n;
0053     ngx_err_t      err;
0054     ngx_buf_t     *file;
0055     ngx_event_t   *wev;
0056     ngx_chain_t   *cl;
0057     ngx_iovec_t    header;
0058     struct iovec   headers[NGX_IOVS_PREALLOCATE];
0059 
0060     wev = c->write;
0061 
0062     if (!wev->ready) {
0063         return in;
0064     }
0065 
0066 
0067     /* the maximum limit size is 2G-1 - the page size */
0068 
0069     if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {
0070         limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;
0071     }
0072 
0073 
0074     send = 0;
0075 
0076     header.iovs = headers;
0077     header.nalloc = NGX_IOVS_PREALLOCATE;
0078 
0079     for ( ;; ) {
0080         prev_send = send;
0081 
0082         /* create the iovec and coalesce the neighbouring bufs */
0083 
0084         cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);
0085 
0086         if (cl == NGX_CHAIN_ERROR) {
0087             return NGX_CHAIN_ERROR;
0088         }
0089 
0090         send += header.size;
0091 
0092         /* set TCP_CORK if there is a header before a file */
0093 
0094         if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
0095             && header.count != 0
0096             && cl
0097             && cl->buf->in_file)
0098         {
0099             /* the TCP_CORK and TCP_NODELAY are mutually exclusive */
0100 
0101             if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {
0102 
0103                 tcp_nodelay = 0;
0104 
0105                 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
0106                                (const void *) &tcp_nodelay, sizeof(int)) == -1)
0107                 {
0108                     err = ngx_socket_errno;
0109 
0110                     /*
0111                      * there is a tiny chance to be interrupted, however,
0112                      * we continue a processing with the TCP_NODELAY
0113                      * and without the TCP_CORK
0114                      */
0115 
0116                     if (err != NGX_EINTR) {
0117                         wev->error = 1;
0118                         ngx_connection_error(c, err,
0119                                              "setsockopt(TCP_NODELAY) failed");
0120                         return NGX_CHAIN_ERROR;
0121                     }
0122 
0123                 } else {
0124                     c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;
0125 
0126                     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
0127                                    "no tcp_nodelay");
0128                 }
0129             }
0130 
0131             if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
0132 
0133                 if (ngx_tcp_nopush(c->fd) == -1) {
0134                     err = ngx_socket_errno;
0135 
0136                     /*
0137                      * there is a tiny chance to be interrupted, however,
0138                      * we continue a processing without the TCP_CORK
0139                      */
0140 
0141                     if (err != NGX_EINTR) {
0142                         wev->error = 1;
0143                         ngx_connection_error(c, err,
0144                                              ngx_tcp_nopush_n " failed");
0145                         return NGX_CHAIN_ERROR;
0146                     }
0147 
0148                 } else {
0149                     c->tcp_nopush = NGX_TCP_NOPUSH_SET;
0150 
0151                     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
0152                                    "tcp_nopush");
0153                 }
0154             }
0155         }
0156 
0157         /* get the file buf */
0158 
0159         if (header.count == 0 && cl && cl->buf->in_file && send < limit) {
0160             file = cl->buf;
0161 
0162             /* coalesce the neighbouring file bufs */
0163 
0164             file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);
0165 
0166             send += file_size;
0167 #if 1
0168             if (file_size == 0) {
0169                 ngx_debug_point();
0170                 return NGX_CHAIN_ERROR;
0171             }
0172 #endif
0173 
0174             n = ngx_linux_sendfile(c, file, file_size);
0175 
0176             if (n == NGX_ERROR) {
0177                 return NGX_CHAIN_ERROR;
0178             }
0179 
0180             if (n == NGX_DONE) {
0181                 /* thread task posted */
0182                 return in;
0183             }
0184 
0185             sent = (n == NGX_AGAIN) ? 0 : n;
0186 
0187         } else {
0188             n = ngx_writev(c, &header);
0189 
0190             if (n == NGX_ERROR) {
0191                 return NGX_CHAIN_ERROR;
0192             }
0193 
0194             sent = (n == NGX_AGAIN) ? 0 : n;
0195         }
0196 
0197         c->sent += sent;
0198 
0199         in = ngx_chain_update_sent(in, sent);
0200 
0201         if (n == NGX_AGAIN) {
0202             wev->ready = 0;
0203             return in;
0204         }
0205 
0206         if ((size_t) (send - prev_send) != sent) {
0207 
0208             /*
0209              * sendfile() on Linux 4.3+ might be interrupted at any time,
0210              * and provides no indication if it was interrupted or not,
0211              * so we have to retry till an explicit EAGAIN
0212              *
0213              * sendfile() in threads can also report less bytes written
0214              * than we are prepared to send now, since it was started in
0215              * some point in the past, so we again have to retry
0216              */
0217 
0218             send = prev_send + sent;
0219             continue;
0220         }
0221 
0222         if (send >= limit || in == NULL) {
0223             return in;
0224         }
0225     }
0226 }
0227 
0228 
0229 static ssize_t
0230 ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
0231 {
0232 #if (NGX_HAVE_SENDFILE64)
0233     off_t      offset;
0234 #else
0235     int32_t    offset;
0236 #endif
0237     ssize_t    n;
0238     ngx_err_t  err;
0239 
0240 #if (NGX_THREADS)
0241 
0242     if (file->file->thread_handler) {
0243         return ngx_linux_sendfile_thread(c, file, size);
0244     }
0245 
0246 #endif
0247 
0248 #if (NGX_HAVE_SENDFILE64)
0249     offset = file->file_pos;
0250 #else
0251     offset = (int32_t) file->file_pos;
0252 #endif
0253 
0254 eintr:
0255 
0256     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
0257                    "sendfile: @%O %uz", file->file_pos, size);
0258 
0259     n = sendfile(c->fd, file->file->fd, &offset, size);
0260 
0261     if (n == -1) {
0262         err = ngx_errno;
0263 
0264         switch (err) {
0265         case NGX_EAGAIN:
0266             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
0267                            "sendfile() is not ready");
0268             return NGX_AGAIN;
0269 
0270         case NGX_EINTR:
0271             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
0272                            "sendfile() was interrupted");
0273             goto eintr;
0274 
0275         default:
0276             c->write->error = 1;
0277             ngx_connection_error(c, err, "sendfile() failed");
0278             return NGX_ERROR;
0279         }
0280     }
0281 
0282     if (n == 0) {
0283         /*
0284          * if sendfile returns zero, then someone has truncated the file,
0285          * so the offset became beyond the end of the file
0286          */
0287 
0288         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
0289                       "sendfile() reported that \"%s\" was truncated at %O",
0290                       file->file->name.data, file->file_pos);
0291 
0292         return NGX_ERROR;
0293     }
0294 
0295     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %z of %uz @%O",
0296                    n, size, file->file_pos);
0297 
0298     return n;
0299 }
0300 
0301 
0302 #if (NGX_THREADS)
0303 
0304 typedef struct {
0305     ngx_buf_t     *file;
0306     ngx_socket_t   socket;
0307     size_t         size;
0308 
0309     size_t         sent;
0310     ngx_err_t      err;
0311 } ngx_linux_sendfile_ctx_t;
0312 
0313 
0314 static ssize_t
0315 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size)
0316 {
0317     ngx_event_t               *wev;
0318     ngx_thread_task_t         *task;
0319     ngx_linux_sendfile_ctx_t  *ctx;
0320 
0321     ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0,
0322                    "linux sendfile thread: %d, %uz, %O",
0323                    file->file->fd, size, file->file_pos);
0324 
0325     task = c->sendfile_task;
0326 
0327     if (task == NULL) {
0328         task = ngx_thread_task_alloc(c->pool, sizeof(ngx_linux_sendfile_ctx_t));
0329         if (task == NULL) {
0330             return NGX_ERROR;
0331         }
0332 
0333         task->handler = ngx_linux_sendfile_thread_handler;
0334 
0335         c->sendfile_task = task;
0336     }
0337 
0338     ctx = task->ctx;
0339     wev = c->write;
0340 
0341     if (task->event.complete) {
0342         task->event.complete = 0;
0343 
0344         if (ctx->err == NGX_EAGAIN) {
0345             /*
0346              * if wev->complete is set, this means that a write event
0347              * happened while we were waiting for the thread task, so
0348              * we have to retry sending even on EAGAIN
0349              */
0350 
0351             if (wev->complete) {
0352                 return 0;
0353             }
0354 
0355             return NGX_AGAIN;
0356         }
0357 
0358         if (ctx->err) {
0359             wev->error = 1;
0360             ngx_connection_error(c, ctx->err, "sendfile() failed");
0361             return NGX_ERROR;
0362         }
0363 
0364         if (ctx->sent == 0) {
0365             /*
0366              * if sendfile returns zero, then someone has truncated the file,
0367              * so the offset became beyond the end of the file
0368              */
0369 
0370             ngx_log_error(NGX_LOG_ALERT, c->log, 0,
0371                           "sendfile() reported that \"%s\" was truncated at %O",
0372                           file->file->name.data, file->file_pos);
0373 
0374             return NGX_ERROR;
0375         }
0376 
0377         return ctx->sent;
0378     }
0379 
0380     if (task->event.active && ctx->file == file) {
0381         /*
0382          * tolerate duplicate calls; they can happen due to subrequests
0383          * or multiple calls of the next body filter from a filter
0384          */
0385 
0386         return NGX_DONE;
0387     }
0388 
0389     ctx->file = file;
0390     ctx->socket = c->fd;
0391     ctx->size = size;
0392 
0393     wev->complete = 0;
0394 
0395     if (file->file->thread_handler(task, file->file) != NGX_OK) {
0396         return NGX_ERROR;
0397     }
0398 
0399     return NGX_DONE;
0400 }
0401 
0402 
0403 static void
0404 ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log)
0405 {
0406     ngx_linux_sendfile_ctx_t *ctx = data;
0407 
0408     off_t       offset;
0409     ssize_t     n;
0410     ngx_buf_t  *file;
0411 
0412     ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "linux sendfile thread handler");
0413 
0414     file = ctx->file;
0415     offset = file->file_pos;
0416 
0417 again:
0418 
0419     n = sendfile(ctx->socket, file->file->fd, &offset, ctx->size);
0420 
0421     if (n == -1) {
0422         ctx->err = ngx_errno;
0423 
0424     } else {
0425         ctx->sent = n;
0426         ctx->err = 0;
0427     }
0428 
0429 #if 0
0430     ngx_time_update();
0431 #endif
0432 
0433     ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
0434                    "sendfile: %z (err: %d) of %uz @%O",
0435                    n, ctx->err, ctx->size, file->file_pos);
0436 
0437     if (ctx->err == NGX_EINTR) {
0438         goto again;
0439     }
0440 }
0441 
0442 #endif /* NGX_THREADS */