xref: /unit/src/nxt_linux_sendfile.c (revision 494)
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 
651Sigor@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 
9813Sigor@sysoev.ru     nxt_debug(c->socket.task, "sendfile(%d, %FD, @%O, %uz)",
9913Sigor@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 
107*494Spluknet@nginx.com     nxt_debug(c->socket.task, "sendfile(): %z", 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;
12113Sigor@sysoev.ru             nxt_log(c->socket.task, nxt_socket_error_level(err),
12213Sigor@sysoev.ru                     "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"",
12313Sigor@sysoev.ru                     c->socket.fd, fb->file->fd, fb->file_pos, size,
12413Sigor@sysoev.ru                     err, fb->file->name);
1250Sigor@sysoev.ru 
1260Sigor@sysoev.ru             return NXT_ERROR;
1270Sigor@sysoev.ru         }
1280Sigor@sysoev.ru 
12913Sigor@sysoev.ru         nxt_debug(c->socket.task, "sendfile() %E", err);
1300Sigor@sysoev.ru 
1310Sigor@sysoev.ru         return 0;
1320Sigor@sysoev.ru     }
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru     if (n < (ssize_t) size) {
1350Sigor@sysoev.ru         c->socket.write_ready = 0;
1360Sigor@sysoev.ru     }
1370Sigor@sysoev.ru 
1380Sigor@sysoev.ru     return n;
1390Sigor@sysoev.ru }
1400Sigor@sysoev.ru 
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru static ssize_t
1430Sigor@sysoev.ru nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
1440Sigor@sysoev.ru {
1450Sigor@sysoev.ru     ssize_t    n;
1460Sigor@sysoev.ru     nxt_err_t  err;
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru     n = send(c->socket.fd, buf, size, flags);
1490Sigor@sysoev.ru 
1500Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
1510Sigor@sysoev.ru 
15213Sigor@sysoev.ru     nxt_debug(c->socket.task, "send(%d, %p, %uz, 0x%uXi): %z",
15313Sigor@sysoev.ru               c->socket.fd, buf, size, flags, n);
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru     if (n == -1) {
1560Sigor@sysoev.ru         switch (err) {
1570Sigor@sysoev.ru 
1580Sigor@sysoev.ru         case NXT_EAGAIN:
1590Sigor@sysoev.ru             c->socket.write_ready = 0;
1600Sigor@sysoev.ru             break;
1610Sigor@sysoev.ru 
1620Sigor@sysoev.ru         case NXT_EINTR:
1630Sigor@sysoev.ru             break;
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru         default:
1660Sigor@sysoev.ru             c->socket.error = err;
16713Sigor@sysoev.ru             nxt_log(c->socket.task, nxt_socket_error_level(err),
16813Sigor@sysoev.ru                     "send(%d, %p, %uz, 0x%uXi) failed %E",
16913Sigor@sysoev.ru                     c->socket.fd, buf, size, flags, err);
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru             return NXT_ERROR;
1720Sigor@sysoev.ru         }
1730Sigor@sysoev.ru 
17413Sigor@sysoev.ru         nxt_debug(c->socket.task, "send() %E", err);
1750Sigor@sysoev.ru 
1760Sigor@sysoev.ru         return 0;
1770Sigor@sysoev.ru     }
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru     if (n < (ssize_t) size) {
1800Sigor@sysoev.ru         c->socket.write_ready = 0;
1810Sigor@sysoev.ru     }
1820Sigor@sysoev.ru 
1830Sigor@sysoev.ru     return n;
1840Sigor@sysoev.ru }
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru static ssize_t
1880Sigor@sysoev.ru nxt_linux_sendmsg(nxt_event_conn_t *c, nxt_sendbuf_coalesce_t *sb,
1890Sigor@sysoev.ru     nxt_uint_t niov, nxt_uint_t flags)
1900Sigor@sysoev.ru {
1910Sigor@sysoev.ru     ssize_t        n;
1920Sigor@sysoev.ru     nxt_err_t      err;
1930Sigor@sysoev.ru     struct msghdr  msg;
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru     msg.msg_name = NULL;
1960Sigor@sysoev.ru     msg.msg_namelen = 0;
1970Sigor@sysoev.ru     msg.msg_iov = sb->iobuf;
1980Sigor@sysoev.ru     msg.msg_iovlen = niov;
1990Sigor@sysoev.ru     msg.msg_control = NULL;
2000Sigor@sysoev.ru     msg.msg_controllen = 0;
2010Sigor@sysoev.ru     msg.msg_flags = 0;
2020Sigor@sysoev.ru 
2030Sigor@sysoev.ru     n = sendmsg(c->socket.fd, &msg, flags);
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
2060Sigor@sysoev.ru 
207*494Spluknet@nginx.com     nxt_debug(c->socket.task, "sendmsg(%d, %ui, 0x%uXi): %z",
20813Sigor@sysoev.ru               c->socket.fd, niov, flags, n);
2090Sigor@sysoev.ru 
2100Sigor@sysoev.ru     if (n == -1) {
2110Sigor@sysoev.ru         switch (err) {
2120Sigor@sysoev.ru 
2130Sigor@sysoev.ru         case NXT_EAGAIN:
2140Sigor@sysoev.ru             c->socket.write_ready = 0;
2150Sigor@sysoev.ru             break;
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru         case NXT_EINTR:
2180Sigor@sysoev.ru             break;
2190Sigor@sysoev.ru 
2200Sigor@sysoev.ru         default:
2210Sigor@sysoev.ru             c->socket.error = err;
22213Sigor@sysoev.ru             nxt_log(c->socket.task, nxt_socket_error_level(err),
22313Sigor@sysoev.ru                     "sendmsg(%d, %ui, 0x%uXi) failed %E",
22413Sigor@sysoev.ru                     c->socket.fd, niov, flags, err);
2250Sigor@sysoev.ru 
2260Sigor@sysoev.ru             return NXT_ERROR;
2270Sigor@sysoev.ru         }
2280Sigor@sysoev.ru 
22913Sigor@sysoev.ru         nxt_debug(c->socket.task, "sendmsg() %E", err);
2300Sigor@sysoev.ru 
2310Sigor@sysoev.ru         return 0;
2320Sigor@sysoev.ru     }
2330Sigor@sysoev.ru 
2340Sigor@sysoev.ru     if (n < (ssize_t) sb->size) {
2350Sigor@sysoev.ru         c->socket.write_ready = 0;
2360Sigor@sysoev.ru     }
2370Sigor@sysoev.ru 
2380Sigor@sysoev.ru     return n;
2390Sigor@sysoev.ru }
240