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 98*13Sigor@sysoev.ru nxt_debug(c->socket.task, "sendfile(%d, %FD, @%O, %uz)", 99*13Sigor@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*13Sigor@sysoev.ru nxt_debug(c->socket.task, "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; 121*13Sigor@sysoev.ru nxt_log(c->socket.task, nxt_socket_error_level(err), 122*13Sigor@sysoev.ru "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"", 123*13Sigor@sysoev.ru c->socket.fd, fb->file->fd, fb->file_pos, size, 124*13Sigor@sysoev.ru err, fb->file->name); 1250Sigor@sysoev.ru 1260Sigor@sysoev.ru return NXT_ERROR; 1270Sigor@sysoev.ru } 1280Sigor@sysoev.ru 129*13Sigor@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 152*13Sigor@sysoev.ru nxt_debug(c->socket.task, "send(%d, %p, %uz, 0x%uXi): %z", 153*13Sigor@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; 167*13Sigor@sysoev.ru nxt_log(c->socket.task, nxt_socket_error_level(err), 168*13Sigor@sysoev.ru "send(%d, %p, %uz, 0x%uXi) failed %E", 169*13Sigor@sysoev.ru c->socket.fd, buf, size, flags, err); 1700Sigor@sysoev.ru 1710Sigor@sysoev.ru return NXT_ERROR; 1720Sigor@sysoev.ru } 1730Sigor@sysoev.ru 174*13Sigor@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*13Sigor@sysoev.ru nxt_debug(c->socket.task, "sendmsg(%d, %ui, 0x%uXi): %d", 208*13Sigor@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; 222*13Sigor@sysoev.ru nxt_log(c->socket.task, nxt_socket_error_level(err), 223*13Sigor@sysoev.ru "sendmsg(%d, %ui, 0x%uXi) failed %E", 224*13Sigor@sysoev.ru c->socket.fd, niov, flags, err); 2250Sigor@sysoev.ru 2260Sigor@sysoev.ru return NXT_ERROR; 2270Sigor@sysoev.ru } 2280Sigor@sysoev.ru 229*13Sigor@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