/src/unit/src/nxt_linux_sendfile.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #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 | 0 | { |
49 | 0 | size_t size; |
50 | 0 | ssize_t n; |
51 | 0 | nxt_buf_t *fb; |
52 | 0 | nxt_err_t err; |
53 | 0 | nxt_off_t offset; |
54 | 0 | nxt_uint_t niov, flags; |
55 | 0 | struct iovec iov[NXT_IOBUF_MAX]; |
56 | 0 | nxt_sendbuf_coalesce_t sb; |
57 | |
|
58 | 0 | sb.buf = b; |
59 | 0 | sb.iobuf = iov; |
60 | 0 | sb.nmax = NXT_IOBUF_MAX; |
61 | 0 | sb.sync = 0; |
62 | 0 | sb.size = 0; |
63 | 0 | sb.limit = limit; |
64 | |
|
65 | 0 | niov = nxt_sendbuf_mem_coalesce(c->socket.task, &sb); |
66 | |
|
67 | 0 | if (niov == 0 && sb.sync) { |
68 | 0 | return 0; |
69 | 0 | } |
70 | | |
71 | 0 | fb = (sb.buf != NULL && nxt_buf_is_file(sb.buf)) ? sb.buf : NULL; |
72 | |
|
73 | 0 | if (niov != 0) { |
74 | |
|
75 | 0 | flags = MSG_NOSIGNAL; |
76 | |
|
77 | 0 | if (fb != NULL) { |
78 | | /* |
79 | | * The Linux-specific MSG_MORE flag is cheaper |
80 | | * than additional setsockopt(TCP_CORK) syscall. |
81 | | */ |
82 | 0 | flags |= MSG_MORE; |
83 | 0 | } |
84 | |
|
85 | 0 | if (niov == 1) { |
86 | | /* |
87 | | * Disposal of surplus kernel msghdr |
88 | | * and iovec copy-in operations. |
89 | | */ |
90 | 0 | return nxt_linux_send(c, iov->iov_base, iov->iov_len, flags); |
91 | 0 | } |
92 | | |
93 | 0 | return nxt_linux_sendmsg(c, &sb, niov, flags); |
94 | 0 | } |
95 | | |
96 | 0 | size = nxt_sendbuf_file_coalesce(&sb); |
97 | |
|
98 | 0 | nxt_debug(c->socket.task, "sendfile(%d, %FD, @%O, %uz)", |
99 | 0 | c->socket.fd, fb->file->fd, fb->file_pos, size); |
100 | |
|
101 | 0 | offset = fb->file_pos; |
102 | |
|
103 | 0 | n = nxt_sys_sendfile(c->socket.fd, fb->file->fd, &offset, size); |
104 | |
|
105 | 0 | err = (n == -1) ? nxt_errno : 0; |
106 | |
|
107 | 0 | nxt_debug(c->socket.task, "sendfile(): %z", n); |
108 | |
|
109 | 0 | if (n == -1) { |
110 | 0 | switch (err) { |
111 | | |
112 | 0 | case NXT_EAGAIN: |
113 | 0 | c->socket.write_ready = 0; |
114 | 0 | break; |
115 | | |
116 | 0 | case NXT_EINTR: |
117 | 0 | break; |
118 | | |
119 | 0 | default: |
120 | 0 | c->socket.error = err; |
121 | 0 | nxt_log(c->socket.task, nxt_socket_error_level(err), |
122 | 0 | "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"", |
123 | 0 | c->socket.fd, fb->file->fd, fb->file_pos, size, |
124 | 0 | err, fb->file->name); |
125 | |
|
126 | 0 | return NXT_ERROR; |
127 | 0 | } |
128 | | |
129 | 0 | nxt_debug(c->socket.task, "sendfile() %E", err); |
130 | |
|
131 | 0 | return 0; |
132 | 0 | } |
133 | | |
134 | 0 | if (n < (ssize_t) size) { |
135 | 0 | c->socket.write_ready = 0; |
136 | 0 | } |
137 | |
|
138 | 0 | return n; |
139 | 0 | } |
140 | | |
141 | | |
142 | | static ssize_t |
143 | | nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size, nxt_uint_t flags) |
144 | 0 | { |
145 | 0 | ssize_t n; |
146 | 0 | nxt_err_t err; |
147 | |
|
148 | 0 | n = send(c->socket.fd, buf, size, flags); |
149 | |
|
150 | 0 | err = (n == -1) ? nxt_errno : 0; |
151 | |
|
152 | 0 | nxt_debug(c->socket.task, "send(%d, %p, %uz, 0x%uXi): %z", |
153 | 0 | c->socket.fd, buf, size, flags, n); |
154 | |
|
155 | 0 | if (n == -1) { |
156 | 0 | switch (err) { |
157 | | |
158 | 0 | case NXT_EAGAIN: |
159 | 0 | c->socket.write_ready = 0; |
160 | 0 | break; |
161 | | |
162 | 0 | case NXT_EINTR: |
163 | 0 | break; |
164 | | |
165 | 0 | default: |
166 | 0 | c->socket.error = err; |
167 | 0 | nxt_log(c->socket.task, nxt_socket_error_level(err), |
168 | 0 | "send(%d, %p, %uz, 0x%uXi) failed %E", |
169 | 0 | c->socket.fd, buf, size, flags, err); |
170 | |
|
171 | 0 | return NXT_ERROR; |
172 | 0 | } |
173 | | |
174 | 0 | nxt_debug(c->socket.task, "send() %E", err); |
175 | |
|
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | 0 | if (n < (ssize_t) size) { |
180 | 0 | c->socket.write_ready = 0; |
181 | 0 | } |
182 | |
|
183 | 0 | return n; |
184 | 0 | } |
185 | | |
186 | | |
187 | | static ssize_t |
188 | | nxt_linux_sendmsg(nxt_event_conn_t *c, nxt_sendbuf_coalesce_t *sb, |
189 | | nxt_uint_t niov, nxt_uint_t flags) |
190 | 0 | { |
191 | 0 | ssize_t n; |
192 | 0 | nxt_err_t err; |
193 | 0 | struct msghdr msg; |
194 | |
|
195 | 0 | msg.msg_name = NULL; |
196 | 0 | msg.msg_namelen = 0; |
197 | 0 | msg.msg_iov = sb->iobuf; |
198 | 0 | msg.msg_iovlen = niov; |
199 | 0 | msg.msg_control = NULL; |
200 | 0 | msg.msg_controllen = 0; |
201 | 0 | msg.msg_flags = 0; |
202 | |
|
203 | 0 | n = sendmsg(c->socket.fd, &msg, flags); |
204 | |
|
205 | 0 | err = (n == -1) ? nxt_errno : 0; |
206 | |
|
207 | 0 | nxt_debug(c->socket.task, "sendmsg(%d, %ui, 0x%uXi): %z", |
208 | 0 | c->socket.fd, niov, flags, n); |
209 | |
|
210 | 0 | if (n == -1) { |
211 | 0 | switch (err) { |
212 | | |
213 | 0 | case NXT_EAGAIN: |
214 | 0 | c->socket.write_ready = 0; |
215 | 0 | break; |
216 | | |
217 | 0 | case NXT_EINTR: |
218 | 0 | break; |
219 | | |
220 | 0 | default: |
221 | 0 | c->socket.error = err; |
222 | 0 | nxt_log(c->socket.task, nxt_socket_error_level(err), |
223 | 0 | "sendmsg(%d, %ui, 0x%uXi) failed %E", |
224 | 0 | c->socket.fd, niov, flags, err); |
225 | |
|
226 | 0 | return NXT_ERROR; |
227 | 0 | } |
228 | | |
229 | 0 | nxt_debug(c->socket.task, "sendmsg() %E", err); |
230 | |
|
231 | 0 | return 0; |
232 | 0 | } |
233 | | |
234 | 0 | if (n < (ssize_t) sb->size) { |
235 | 0 | c->socket.write_ready = 0; |
236 | 0 | } |
237 | |
|
238 | 0 | return n; |
239 | 0 | } |