1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 /*
11  * sendfile() has been introduced in Linux 2.2.
12  * It supported 32-bit offsets only.
13  *
14  * Linux 2.4.21 has introduced sendfile64().  However, even on 64-bit
15  * platforms it returns EINVAL if the count argument is more than 2G-1 bytes.
16  * In Linux 2.6.17 sendfile() has been internally changed to splice()
17  * and this limitation has gone.
18  */
19 
20 #ifdef NXT_TEST_BUILD_LINUX_SENDFILE
21 
22 #define MSG_NOSIGNAL      0x4000
23 #define MSG_MORE          0x8000
24 
25 ssize_t nxt_linux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
26     size_t limit);
27 
nxt_sys_sendfile(int out_fd,int in_fd,off_t * offset,size_t count)28 static ssize_t nxt_sys_sendfile(int out_fd, int in_fd, off_t *offset,
29     size_t count)
30 {
31     return -1;
32 }
33 
34 #else
35 #define nxt_sys_sendfile  sendfile
36 #endif
37 
38 
39 static ssize_t nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size,
40     nxt_uint_t flags);
41 static ssize_t nxt_linux_sendmsg(nxt_event_conn_t *c,
42     nxt_sendbuf_coalesce_t *sb, nxt_uint_t niov, nxt_uint_t flags);
43 
44 
45 ssize_t
nxt_linux_event_conn_io_sendfile(nxt_event_conn_t * c,nxt_buf_t * b,size_t limit)46 nxt_linux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
47     size_t limit)
48 {
49     size_t                  size;
50     ssize_t                 n;
51     nxt_buf_t               *fb;
52     nxt_err_t               err;
53     nxt_off_t               offset;
54     nxt_uint_t              niov, flags;
55     struct iovec            iov[NXT_IOBUF_MAX];
56     nxt_sendbuf_coalesce_t  sb;
57 
58     sb.buf = b;
59     sb.iobuf = iov;
60     sb.nmax = NXT_IOBUF_MAX;
61     sb.sync = 0;
62     sb.size = 0;
63     sb.limit = limit;
64 
65     niov = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);
66 
67     if (niov == 0 && sb.sync) {
68         return 0;
69     }
70 
71     fb = (sb.buf != NULL && nxt_buf_is_file(sb.buf)) ? sb.buf : NULL;
72 
73     if (niov != 0) {
74 
75         flags = MSG_NOSIGNAL;
76 
77         if (fb != NULL) {
78             /*
79              * The Linux-specific MSG_MORE flag is cheaper
80              * than additional setsockopt(TCP_CORK) syscall.
81              */
82             flags |= MSG_MORE;
83         }
84 
85         if (niov == 1) {
86             /*
87              * Disposal of surplus kernel msghdr
88              * and iovec copy-in operations.
89              */
90             return nxt_linux_send(c, iov->iov_base, iov->iov_len, flags);
91         }
92 
93         return nxt_linux_sendmsg(c, &sb, niov, flags);
94     }
95 
96     size = nxt_sendbuf_file_coalesce(&sb);
97 
98     nxt_debug(c->socket.task, "sendfile(%d, %FD, @%O, %uz)",
99               c->socket.fd, fb->file->fd, fb->file_pos, size);
100 
101     offset = fb->file_pos;
102 
103     n = nxt_sys_sendfile(c->socket.fd, fb->file->fd, &offset, size);
104 
105     err = (n == -1) ? nxt_errno : 0;
106 
107     nxt_debug(c->socket.task, "sendfile(): %z", n);
108 
109     if (n == -1) {
110         switch (err) {
111 
112         case NXT_EAGAIN:
113             c->socket.write_ready = 0;
114             break;
115 
116         case NXT_EINTR:
117             break;
118 
119         default:
120             c->socket.error = err;
121             nxt_log(c->socket.task, nxt_socket_error_level(err),
122                     "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"",
123                     c->socket.fd, fb->file->fd, fb->file_pos, size,
124                     err, fb->file->name);
125 
126             return NXT_ERROR;
127         }
128 
129         nxt_debug(c->socket.task, "sendfile() %E", err);
130 
131         return 0;
132     }
133 
134     if (n < (ssize_t) size) {
135         c->socket.write_ready = 0;
136     }
137 
138     return n;
139 }
140 
141 
142 static ssize_t
nxt_linux_send(nxt_event_conn_t * c,void * buf,size_t size,nxt_uint_t flags)143 nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
144 {
145     ssize_t    n;
146     nxt_err_t  err;
147 
148     n = send(c->socket.fd, buf, size, flags);
149 
150     err = (n == -1) ? nxt_errno : 0;
151 
152     nxt_debug(c->socket.task, "send(%d, %p, %uz, 0x%uXi): %z",
153               c->socket.fd, buf, size, flags, n);
154 
155     if (n == -1) {
156         switch (err) {
157 
158         case NXT_EAGAIN:
159             c->socket.write_ready = 0;
160             break;
161 
162         case NXT_EINTR:
163             break;
164 
165         default:
166             c->socket.error = err;
167             nxt_log(c->socket.task, nxt_socket_error_level(err),
168                     "send(%d, %p, %uz, 0x%uXi) failed %E",
169                     c->socket.fd, buf, size, flags, err);
170 
171             return NXT_ERROR;
172         }
173 
174         nxt_debug(c->socket.task, "send() %E", err);
175 
176         return 0;
177     }
178 
179     if (n < (ssize_t) size) {
180         c->socket.write_ready = 0;
181     }
182 
183     return n;
184 }
185 
186 
187 static ssize_t
nxt_linux_sendmsg(nxt_event_conn_t * c,nxt_sendbuf_coalesce_t * sb,nxt_uint_t niov,nxt_uint_t flags)188 nxt_linux_sendmsg(nxt_event_conn_t *c, nxt_sendbuf_coalesce_t *sb,
189     nxt_uint_t niov, nxt_uint_t flags)
190 {
191     ssize_t        n;
192     nxt_err_t      err;
193     struct msghdr  msg;
194 
195     msg.msg_name = NULL;
196     msg.msg_namelen = 0;
197     msg.msg_iov = sb->iobuf;
198     msg.msg_iovlen = niov;
199     msg.msg_control = NULL;
200     msg.msg_controllen = 0;
201     msg.msg_flags = 0;
202 
203     n = sendmsg(c->socket.fd, &msg, flags);
204 
205     err = (n == -1) ? nxt_errno : 0;
206 
207     nxt_debug(c->socket.task, "sendmsg(%d, %ui, 0x%uXi): %z",
208               c->socket.fd, niov, flags, n);
209 
210     if (n == -1) {
211         switch (err) {
212 
213         case NXT_EAGAIN:
214             c->socket.write_ready = 0;
215             break;
216 
217         case NXT_EINTR:
218             break;
219 
220         default:
221             c->socket.error = err;
222             nxt_log(c->socket.task, nxt_socket_error_level(err),
223                     "sendmsg(%d, %ui, 0x%uXi) failed %E",
224                     c->socket.fd, niov, flags, err);
225 
226             return NXT_ERROR;
227         }
228 
229         nxt_debug(c->socket.task, "sendmsg() %E", err);
230 
231         return 0;
232     }
233 
234     if (n < (ssize_t) sb->size) {
235         c->socket.write_ready = 0;
236     }
237 
238     return n;
239 }
240