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
nxt_sys_sendfile(int out_fd,int in_fd,off_t * offset,size_t count)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
nxt_linux_event_conn_io_sendfile(nxt_event_conn_t * c,nxt_buf_t * b,size_t limit)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
nxt_linux_send(nxt_event_conn_t * c,void * buf,size_t size,nxt_uint_t flags)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
nxt_linux_sendmsg(nxt_event_conn_t * c,nxt_sendbuf_coalesce_t * sb,nxt_uint_t niov,nxt_uint_t flags)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