10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru /*
110Sigor@sysoev.ru  * sendfile() has been introduced in Linux 2.2.
120Sigor@sysoev.ru  * It supported 32-bit offsets only.
130Sigor@sysoev.ru  *
140Sigor@sysoev.ru  * Linux 2.4.21 has introduced sendfile64().  However, even on 64-bit
150Sigor@sysoev.ru  * platforms it returns EINVAL if the count argument is more than 2G-1 bytes.
160Sigor@sysoev.ru  * In Linux 2.6.17 sendfile() has been internally changed to splice()
170Sigor@sysoev.ru  * and this limitation has gone.
180Sigor@sysoev.ru  */
190Sigor@sysoev.ru 
200Sigor@sysoev.ru #ifdef NXT_TEST_BUILD_LINUX_SENDFILE
210Sigor@sysoev.ru 
220Sigor@sysoev.ru #define MSG_NOSIGNAL      0x4000
230Sigor@sysoev.ru #define MSG_MORE          0x8000
240Sigor@sysoev.ru 
250Sigor@sysoev.ru ssize_t nxt_linux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
260Sigor@sysoev.ru     size_t limit);
270Sigor@sysoev.ru 
280Sigor@sysoev.ru static ssize_t nxt_sys_sendfile(int out_fd, int in_fd, off_t *offset,
290Sigor@sysoev.ru     size_t count)
300Sigor@sysoev.ru {
310Sigor@sysoev.ru     return -1;
320Sigor@sysoev.ru }
330Sigor@sysoev.ru 
340Sigor@sysoev.ru #else
350Sigor@sysoev.ru #define nxt_sys_sendfile  sendfile
360Sigor@sysoev.ru #endif
370Sigor@sysoev.ru 
380Sigor@sysoev.ru 
390Sigor@sysoev.ru static ssize_t nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size,
400Sigor@sysoev.ru     nxt_uint_t flags);
410Sigor@sysoev.ru static ssize_t nxt_linux_sendmsg(nxt_event_conn_t *c,
420Sigor@sysoev.ru     nxt_sendbuf_coalesce_t *sb, nxt_uint_t niov, nxt_uint_t flags);
430Sigor@sysoev.ru 
440Sigor@sysoev.ru 
450Sigor@sysoev.ru ssize_t
460Sigor@sysoev.ru nxt_linux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
470Sigor@sysoev.ru     size_t limit)
480Sigor@sysoev.ru {
490Sigor@sysoev.ru     size_t                  size;
500Sigor@sysoev.ru     ssize_t                 n;
510Sigor@sysoev.ru     nxt_buf_t               *fb;
520Sigor@sysoev.ru     nxt_err_t               err;
530Sigor@sysoev.ru     nxt_off_t               offset;
540Sigor@sysoev.ru     nxt_uint_t              niov, flags;
550Sigor@sysoev.ru     struct iovec            iov[NXT_IOBUF_MAX];
560Sigor@sysoev.ru     nxt_sendbuf_coalesce_t  sb;
570Sigor@sysoev.ru 
580Sigor@sysoev.ru     sb.buf = b;
590Sigor@sysoev.ru     sb.iobuf = iov;
600Sigor@sysoev.ru     sb.nmax = NXT_IOBUF_MAX;
610Sigor@sysoev.ru     sb.sync = 0;
620Sigor@sysoev.ru     sb.size = 0;
630Sigor@sysoev.ru     sb.limit = limit;
640Sigor@sysoev.ru 
65*1Sigor@sysoev.ru     niov = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);
660Sigor@sysoev.ru 
670Sigor@sysoev.ru     if (niov == 0 && sb.sync) {
680Sigor@sysoev.ru         return 0;
690Sigor@sysoev.ru     }
700Sigor@sysoev.ru 
710Sigor@sysoev.ru     fb = (sb.buf != NULL && nxt_buf_is_file(sb.buf)) ? sb.buf : NULL;
720Sigor@sysoev.ru 
730Sigor@sysoev.ru     if (niov != 0) {
740Sigor@sysoev.ru 
750Sigor@sysoev.ru         flags = MSG_NOSIGNAL;
760Sigor@sysoev.ru 
770Sigor@sysoev.ru         if (fb != NULL) {
780Sigor@sysoev.ru             /*
790Sigor@sysoev.ru              * The Linux-specific MSG_MORE flag is cheaper
800Sigor@sysoev.ru              * than additional setsockopt(TCP_CORK) syscall.
810Sigor@sysoev.ru              */
820Sigor@sysoev.ru             flags |= MSG_MORE;
830Sigor@sysoev.ru         }
840Sigor@sysoev.ru 
850Sigor@sysoev.ru         if (niov == 1) {
860Sigor@sysoev.ru             /*
870Sigor@sysoev.ru              * Disposal of surplus kernel msghdr
880Sigor@sysoev.ru              * and iovec copy-in operations.
890Sigor@sysoev.ru              */
900Sigor@sysoev.ru             return nxt_linux_send(c, iov->iov_base, iov->iov_len, flags);
910Sigor@sysoev.ru         }
920Sigor@sysoev.ru 
930Sigor@sysoev.ru         return nxt_linux_sendmsg(c, &sb, niov, flags);
940Sigor@sysoev.ru     }
950Sigor@sysoev.ru 
960Sigor@sysoev.ru     size = nxt_sendbuf_file_coalesce(&sb);
970Sigor@sysoev.ru 
980Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendfile(%d, %FD, @%O, %uz)",
990Sigor@sysoev.ru                   c->socket.fd, fb->file->fd, fb->file_pos, size);
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru     offset = fb->file_pos;
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru     n = nxt_sys_sendfile(c->socket.fd, fb->file->fd, &offset, size);
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
1060Sigor@sysoev.ru 
1070Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendfile(): %d", n);
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru     if (n == -1) {
1100Sigor@sysoev.ru         switch (err) {
1110Sigor@sysoev.ru 
1120Sigor@sysoev.ru         case NXT_EAGAIN:
1130Sigor@sysoev.ru             c->socket.write_ready = 0;
1140Sigor@sysoev.ru             break;
1150Sigor@sysoev.ru 
1160Sigor@sysoev.ru         case NXT_EINTR:
1170Sigor@sysoev.ru             break;
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru         default:
1200Sigor@sysoev.ru             c->socket.error = err;
1210Sigor@sysoev.ru             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
1220Sigor@sysoev.ru                           c->socket.log,
1230Sigor@sysoev.ru                           "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"",
1240Sigor@sysoev.ru                           c->socket.fd, fb->file->fd, fb->file_pos, size,
1250Sigor@sysoev.ru                           err, fb->file->name);
1260Sigor@sysoev.ru 
1270Sigor@sysoev.ru             return NXT_ERROR;
1280Sigor@sysoev.ru         }
1290Sigor@sysoev.ru 
1300Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "sendfile() %E", err);
1310Sigor@sysoev.ru 
1320Sigor@sysoev.ru         return 0;
1330Sigor@sysoev.ru     }
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru     if (n < (ssize_t) size) {
1360Sigor@sysoev.ru         c->socket.write_ready = 0;
1370Sigor@sysoev.ru     }
1380Sigor@sysoev.ru 
1390Sigor@sysoev.ru     return n;
1400Sigor@sysoev.ru }
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru 
1430Sigor@sysoev.ru static ssize_t
1440Sigor@sysoev.ru nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
1450Sigor@sysoev.ru {
1460Sigor@sysoev.ru     ssize_t    n;
1470Sigor@sysoev.ru     nxt_err_t  err;
1480Sigor@sysoev.ru 
1490Sigor@sysoev.ru     n = send(c->socket.fd, buf, size, flags);
1500Sigor@sysoev.ru 
1510Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "send(%d, %p, %uz, 0x%uXi): %z",
1540Sigor@sysoev.ru                   c->socket.fd, buf, size, flags, n);
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru     if (n == -1) {
1570Sigor@sysoev.ru         switch (err) {
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru         case NXT_EAGAIN:
1600Sigor@sysoev.ru             c->socket.write_ready = 0;
1610Sigor@sysoev.ru             break;
1620Sigor@sysoev.ru 
1630Sigor@sysoev.ru         case NXT_EINTR:
1640Sigor@sysoev.ru             break;
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru         default:
1670Sigor@sysoev.ru             c->socket.error = err;
1680Sigor@sysoev.ru             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
1690Sigor@sysoev.ru                           c->socket.log, "send(%d, %p, %uz, 0x%uXi) failed %E",
1700Sigor@sysoev.ru                           c->socket.fd, buf, size, flags, err);
1710Sigor@sysoev.ru 
1720Sigor@sysoev.ru             return NXT_ERROR;
1730Sigor@sysoev.ru         }
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "send() %E", err);
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru         return 0;
1780Sigor@sysoev.ru     }
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru     if (n < (ssize_t) size) {
1810Sigor@sysoev.ru         c->socket.write_ready = 0;
1820Sigor@sysoev.ru     }
1830Sigor@sysoev.ru 
1840Sigor@sysoev.ru     return n;
1850Sigor@sysoev.ru }
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru 
1880Sigor@sysoev.ru static ssize_t
1890Sigor@sysoev.ru nxt_linux_sendmsg(nxt_event_conn_t *c, nxt_sendbuf_coalesce_t *sb,
1900Sigor@sysoev.ru     nxt_uint_t niov, nxt_uint_t flags)
1910Sigor@sysoev.ru {
1920Sigor@sysoev.ru     ssize_t        n;
1930Sigor@sysoev.ru     nxt_err_t      err;
1940Sigor@sysoev.ru     struct msghdr  msg;
1950Sigor@sysoev.ru 
1960Sigor@sysoev.ru     msg.msg_name = NULL;
1970Sigor@sysoev.ru     msg.msg_namelen = 0;
1980Sigor@sysoev.ru     msg.msg_iov = sb->iobuf;
1990Sigor@sysoev.ru     msg.msg_iovlen = niov;
2000Sigor@sysoev.ru     msg.msg_control = NULL;
2010Sigor@sysoev.ru     msg.msg_controllen = 0;
2020Sigor@sysoev.ru     msg.msg_flags = 0;
2030Sigor@sysoev.ru 
2040Sigor@sysoev.ru     n = sendmsg(c->socket.fd, &msg, flags);
2050Sigor@sysoev.ru 
2060Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendmsg(%d, %ui, 0x%uXi): %d",
2090Sigor@sysoev.ru                   c->socket.fd, niov, flags, n);
2100Sigor@sysoev.ru 
2110Sigor@sysoev.ru     if (n == -1) {
2120Sigor@sysoev.ru         switch (err) {
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru         case NXT_EAGAIN:
2150Sigor@sysoev.ru             c->socket.write_ready = 0;
2160Sigor@sysoev.ru             break;
2170Sigor@sysoev.ru 
2180Sigor@sysoev.ru         case NXT_EINTR:
2190Sigor@sysoev.ru             break;
2200Sigor@sysoev.ru 
2210Sigor@sysoev.ru         default:
2220Sigor@sysoev.ru             c->socket.error = err;
2230Sigor@sysoev.ru             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
2240Sigor@sysoev.ru                           c->socket.log, "sendmsg(%d, %ui, 0x%uXi) failed %E",
2250Sigor@sysoev.ru                           c->socket.fd, niov, flags, err);
2260Sigor@sysoev.ru 
2270Sigor@sysoev.ru             return NXT_ERROR;
2280Sigor@sysoev.ru         }
2290Sigor@sysoev.ru 
2300Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "sendmsg() %E", err);
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru         return 0;
2330Sigor@sysoev.ru     }
2340Sigor@sysoev.ru 
2350Sigor@sysoev.ru     if (n < (ssize_t) sb->size) {
2360Sigor@sysoev.ru         c->socket.write_ready = 0;
2370Sigor@sysoev.ru     }
2380Sigor@sysoev.ru 
2390Sigor@sysoev.ru     return n;
2400Sigor@sysoev.ru }
241