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