xref: /unit/src/nxt_linux_sendfile.c (revision 0:a63ceefd6ab0)
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 
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
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(&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_log_debug(c->socket.log, "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_log_debug(c->socket.log, "sendfile(): %d", 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_error(nxt_socket_error_level(err, c->socket.log_error),
122                           c->socket.log,
123                           "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"",
124                           c->socket.fd, fb->file->fd, fb->file_pos, size,
125                           err, fb->file->name);
126 
127             return NXT_ERROR;
128         }
129 
130         nxt_log_debug(c->socket.log, "sendfile() %E", err);
131 
132         return 0;
133     }
134 
135     if (n < (ssize_t) size) {
136         c->socket.write_ready = 0;
137     }
138 
139     return n;
140 }
141 
142 
143 static ssize_t
144 nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
145 {
146     ssize_t    n;
147     nxt_err_t  err;
148 
149     n = send(c->socket.fd, buf, size, flags);
150 
151     err = (n == -1) ? nxt_errno : 0;
152 
153     nxt_log_debug(c->socket.log, "send(%d, %p, %uz, 0x%uXi): %z",
154                   c->socket.fd, buf, size, flags, n);
155 
156     if (n == -1) {
157         switch (err) {
158 
159         case NXT_EAGAIN:
160             c->socket.write_ready = 0;
161             break;
162 
163         case NXT_EINTR:
164             break;
165 
166         default:
167             c->socket.error = err;
168             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
169                           c->socket.log, "send(%d, %p, %uz, 0x%uXi) failed %E",
170                           c->socket.fd, buf, size, flags, err);
171 
172             return NXT_ERROR;
173         }
174 
175         nxt_log_debug(c->socket.log, "send() %E", err);
176 
177         return 0;
178     }
179 
180     if (n < (ssize_t) size) {
181         c->socket.write_ready = 0;
182     }
183 
184     return n;
185 }
186 
187 
188 static ssize_t
189 nxt_linux_sendmsg(nxt_event_conn_t *c, nxt_sendbuf_coalesce_t *sb,
190     nxt_uint_t niov, nxt_uint_t flags)
191 {
192     ssize_t        n;
193     nxt_err_t      err;
194     struct msghdr  msg;
195 
196     msg.msg_name = NULL;
197     msg.msg_namelen = 0;
198     msg.msg_iov = sb->iobuf;
199     msg.msg_iovlen = niov;
200     msg.msg_control = NULL;
201     msg.msg_controllen = 0;
202     msg.msg_flags = 0;
203 
204     n = sendmsg(c->socket.fd, &msg, flags);
205 
206     err = (n == -1) ? nxt_errno : 0;
207 
208     nxt_log_debug(c->socket.log, "sendmsg(%d, %ui, 0x%uXi): %d",
209                   c->socket.fd, niov, flags, n);
210 
211     if (n == -1) {
212         switch (err) {
213 
214         case NXT_EAGAIN:
215             c->socket.write_ready = 0;
216             break;
217 
218         case NXT_EINTR:
219             break;
220 
221         default:
222             c->socket.error = err;
223             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
224                           c->socket.log, "sendmsg(%d, %ui, 0x%uXi) failed %E",
225                           c->socket.fd, niov, flags, err);
226 
227             return NXT_ERROR;
228         }
229 
230         nxt_log_debug(c->socket.log, "sendmsg() %E", err);
231 
232         return 0;
233     }
234 
235     if (n < (ssize_t) sb->size) {
236         c->socket.write_ready = 0;
237     }
238 
239     return n;
240 }
241