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