0
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
|
4 */
|
|
5
|
|
6
|
|
7 #include <ngx_config.h>
|
|
8 #include <ngx_core.h>
|
|
9 #include <ngx_event.h>
|
|
10
|
|
11
|
|
12 /*
|
|
13 * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit
|
28
|
14 * offsets only, and the including <sys/sendfile.h> breaks the compiling,
|
|
15 * if off_t is 64 bit wide. So we use own sendfile() definition, where offset
|
|
16 * parameter is int32_t, and use sendfile() for the file parts below 2G only,
|
|
17 * see src/os/unix/ngx_linux_config.h
|
0
|
18 *
|
232
|
19 * Linux 2.4.21 has the new sendfile64() syscall #239.
|
|
20 *
|
|
21 * On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter
|
|
22 * more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL,
|
|
23 * so we limit it to 2G-1 bytes.
|
0
|
24 */
|
|
25
|
232
|
26 #define NGX_SENDFILE_LIMIT 2147483647L
|
|
27
|
0
|
28
|
146
|
29 #if (IOV_MAX > 64)
|
|
30 #define NGX_HEADERS 64
|
|
31 #else
|
|
32 #define NGX_HEADERS IOV_MAX
|
|
33 #endif
|
0
|
34
|
|
35
|
134
|
36 ngx_chain_t *
|
|
37 ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
|
0
|
38 {
|
138
|
39 int rc, tcp_nodelay;
|
|
40 off_t size, send, prev_send, aligned, sent, fprev;
|
|
41 u_char *prev;
|
|
42 size_t file_size;
|
|
43 ngx_err_t err;
|
|
44 ngx_buf_t *file;
|
|
45 ngx_uint_t eintr, complete;
|
|
46 ngx_array_t header;
|
|
47 ngx_event_t *wev;
|
|
48 ngx_chain_t *cl;
|
|
49 struct iovec *iov, headers[NGX_HEADERS];
|
4
|
50 #if (NGX_HAVE_SENDFILE64)
|
138
|
51 off_t offset;
|
0
|
52 #else
|
138
|
53 int32_t offset;
|
0
|
54 #endif
|
|
55
|
|
56 wev = c->write;
|
|
57
|
|
58 if (!wev->ready) {
|
|
59 return in;
|
|
60 }
|
|
61
|
22
|
62
|
232
|
63 /* the maximum limit size is 2G-1 - the page size */
|
22
|
64
|
324
|
65 if (limit == 0 || limit > (off_t) (NGX_SENDFILE_LIMIT - ngx_pagesize)) {
|
232
|
66 limit = NGX_SENDFILE_LIMIT - ngx_pagesize;
|
22
|
67 }
|
|
68
|
|
69
|
0
|
70 send = 0;
|
|
71
|
|
72 header.elts = headers;
|
|
73 header.size = sizeof(struct iovec);
|
|
74 header.nalloc = NGX_HEADERS;
|
|
75 header.pool = c->pool;
|
|
76
|
|
77 for ( ;; ) {
|
|
78 file = NULL;
|
22
|
79 file_size = 0;
|
0
|
80 eintr = 0;
|
|
81 complete = 0;
|
22
|
82 prev_send = send;
|
0
|
83
|
|
84 header.nelts = 0;
|
|
85
|
|
86 prev = NULL;
|
|
87 iov = NULL;
|
|
88
|
|
89 /* create the iovec and coalesce the neighbouring bufs */
|
|
90
|
|
91 for (cl = in;
|
|
92 cl && header.nelts < IOV_MAX && send < limit;
|
|
93 cl = cl->next)
|
|
94 {
|
|
95 if (ngx_buf_special(cl->buf)) {
|
|
96 continue;
|
|
97 }
|
|
98
|
28
|
99 #if 1
|
|
100 if (!ngx_buf_in_memory(cl->buf) && !cl->buf->in_file) {
|
|
101 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
32
|
102 "zero size buf in sendfile "
|
|
103 "t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
|
104 cl->buf->temporary,
|
|
105 cl->buf->recycled,
|
|
106 cl->buf->in_file,
|
|
107 cl->buf->start,
|
|
108 cl->buf->pos,
|
|
109 cl->buf->last,
|
|
110 cl->buf->file,
|
|
111 cl->buf->file_pos,
|
|
112 cl->buf->file_last);
|
|
113
|
28
|
114 ngx_debug_point();
|
38
|
115
|
|
116 return NGX_CHAIN_ERROR;
|
28
|
117 }
|
|
118 #endif
|
|
119
|
0
|
120 if (!ngx_buf_in_memory_only(cl->buf)) {
|
|
121 break;
|
|
122 }
|
|
123
|
|
124 size = cl->buf->last - cl->buf->pos;
|
|
125
|
|
126 if (send + size > limit) {
|
|
127 size = limit - send;
|
|
128 }
|
|
129
|
|
130 if (prev == cl->buf->pos) {
|
22
|
131 iov->iov_len += (size_t) size;
|
0
|
132
|
|
133 } else {
|
50
|
134 iov = ngx_array_push(&header);
|
|
135 if (iov == NULL) {
|
0
|
136 return NGX_CHAIN_ERROR;
|
|
137 }
|
|
138
|
|
139 iov->iov_base = (void *) cl->buf->pos;
|
22
|
140 iov->iov_len = (size_t) size;
|
0
|
141 }
|
|
142
|
22
|
143 prev = cl->buf->pos + (size_t) size;
|
0
|
144 send += size;
|
|
145 }
|
|
146
|
|
147 /* set TCP_CORK if there is a header before a file */
|
|
148
|
|
149 if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
|
|
150 && header.nelts != 0
|
|
151 && cl
|
|
152 && cl->buf->in_file)
|
|
153 {
|
22
|
154 /* the TCP_CORK and TCP_NODELAY are mutually exclusive */
|
|
155
|
32
|
156 if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {
|
22
|
157
|
|
158 tcp_nodelay = 0;
|
|
159
|
|
160 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
|
|
161 (const void *) &tcp_nodelay, sizeof(int)) == -1)
|
|
162 {
|
|
163 err = ngx_errno;
|
0
|
164
|
22
|
165 /*
|
28
|
166 * there is a tiny chance to be interrupted, however,
|
22
|
167 * we continue a processing with the TCP_NODELAY
|
|
168 * and without the TCP_CORK
|
|
169 */
|
|
170
|
126
|
171 if (err != NGX_EINTR) {
|
22
|
172 wev->error = 1;
|
32
|
173 ngx_connection_error(c, err,
|
22
|
174 "setsockopt(TCP_NODELAY) failed");
|
|
175 return NGX_CHAIN_ERROR;
|
|
176 }
|
|
177
|
|
178 } else {
|
32
|
179 c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;
|
0
|
180
|
22
|
181 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
|
182 "no tcp_nodelay");
|
0
|
183 }
|
22
|
184 }
|
|
185
|
32
|
186 if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
|
22
|
187
|
|
188 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
|
|
189 err = ngx_errno;
|
0
|
190
|
22
|
191 /*
|
28
|
192 * there is a tiny chance to be interrupted, however,
|
22
|
193 * we continue a processing without the TCP_CORK
|
|
194 */
|
0
|
195
|
126
|
196 if (err != NGX_EINTR) {
|
22
|
197 wev->error = 1;
|
|
198 ngx_connection_error(c, err,
|
|
199 ngx_tcp_nopush_n " failed");
|
|
200 return NGX_CHAIN_ERROR;
|
|
201 }
|
|
202
|
|
203 } else {
|
|
204 c->tcp_nopush = NGX_TCP_NOPUSH_SET;
|
126
|
205
|
22
|
206 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
207 "tcp_nopush");
|
|
208 }
|
0
|
209 }
|
|
210 }
|
|
211
|
|
212 /* get the file buf */
|
|
213
|
|
214 if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) {
|
|
215 file = cl->buf;
|
|
216
|
|
217 /* coalesce the neighbouring file bufs */
|
|
218
|
|
219 do {
|
22
|
220 size = cl->buf->file_last - cl->buf->file_pos;
|
0
|
221
|
|
222 if (send + size > limit) {
|
|
223 size = limit - send;
|
|
224
|
|
225 aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
|
298
|
226 & ~((off_t) ngx_pagesize - 1);
|
0
|
227
|
|
228 if (aligned <= cl->buf->file_last) {
|
|
229 size = aligned - cl->buf->file_pos;
|
|
230 }
|
|
231 }
|
|
232
|
22
|
233 file_size += (size_t) size;
|
0
|
234 send += size;
|
|
235 fprev = cl->buf->file_pos + size;
|
|
236 cl = cl->next;
|
|
237
|
|
238 } while (cl
|
|
239 && cl->buf->in_file
|
|
240 && send < limit
|
|
241 && file->file->fd == cl->buf->file->fd
|
|
242 && fprev == cl->buf->file_pos);
|
|
243 }
|
|
244
|
|
245 if (file) {
|
138
|
246 #if 1
|
|
247 if (file_size == 0) {
|
|
248 ngx_debug_point();
|
|
249 return NGX_CHAIN_ERROR;
|
|
250 }
|
|
251 #endif
|
4
|
252 #if (NGX_HAVE_SENDFILE64)
|
0
|
253 offset = file->file_pos;
|
|
254 #else
|
|
255 offset = (int32_t) file->file_pos;
|
|
256 #endif
|
300
|
257
|
|
258 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
259 "sendfile: @%O %uz", file->file_pos, file_size);
|
|
260
|
22
|
261 rc = sendfile(c->fd, file->file->fd, &offset, file_size);
|
0
|
262
|
|
263 if (rc == -1) {
|
|
264 err = ngx_errno;
|
|
265
|
531
|
266 switch (err) {
|
|
267 case NGX_EAGAIN:
|
|
268 break;
|
0
|
269
|
531
|
270 case NGX_EINTR:
|
|
271 eintr = 1;
|
|
272 break;
|
0
|
273
|
531
|
274 default:
|
0
|
275 wev->error = 1;
|
|
276 ngx_connection_error(c, err, "sendfile() failed");
|
|
277 return NGX_CHAIN_ERROR;
|
|
278 }
|
531
|
279
|
|
280 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
|
|
281 "sendfile() is not ready");
|
0
|
282 }
|
|
283
|
|
284 sent = rc > 0 ? rc : 0;
|
|
285
|
|
286 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
22
|
287 "sendfile: %d, @%O %O:%uz",
|
|
288 rc, file->file_pos, sent, file_size);
|
0
|
289
|
|
290 } else {
|
|
291 rc = writev(c->fd, header.elts, header.nelts);
|
|
292
|
|
293 if (rc == -1) {
|
|
294 err = ngx_errno;
|
|
295
|
531
|
296 switch (err) {
|
|
297 case NGX_EAGAIN:
|
|
298 break;
|
0
|
299
|
531
|
300 case NGX_EINTR:
|
|
301 eintr = 1;
|
|
302 break;
|
0
|
303
|
531
|
304 default:
|
0
|
305 wev->error = 1;
|
|
306 ngx_connection_error(c, err, "writev() failed");
|
126
|
307 return NGX_CHAIN_ERROR;
|
0
|
308 }
|
531
|
309
|
|
310 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
|
|
311 "writev() not ready");
|
0
|
312 }
|
|
313
|
|
314 sent = rc > 0 ? rc : 0;
|
|
315
|
22
|
316 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %O", sent);
|
0
|
317 }
|
|
318
|
22
|
319 if (send - prev_send == sent) {
|
0
|
320 complete = 1;
|
|
321 }
|
|
322
|
|
323 c->sent += sent;
|
|
324
|
|
325 for (cl = in; cl; cl = cl->next) {
|
|
326
|
|
327 if (ngx_buf_special(cl->buf)) {
|
|
328 continue;
|
|
329 }
|
|
330
|
|
331 if (sent == 0) {
|
|
332 break;
|
|
333 }
|
|
334
|
|
335 size = ngx_buf_size(cl->buf);
|
|
336
|
|
337 if (sent >= size) {
|
|
338 sent -= size;
|
|
339
|
|
340 if (ngx_buf_in_memory(cl->buf)) {
|
|
341 cl->buf->pos = cl->buf->last;
|
|
342 }
|
|
343
|
|
344 if (cl->buf->in_file) {
|
|
345 cl->buf->file_pos = cl->buf->file_last;
|
|
346 }
|
|
347
|
|
348 continue;
|
|
349 }
|
|
350
|
|
351 if (ngx_buf_in_memory(cl->buf)) {
|
22
|
352 cl->buf->pos += (size_t) sent;
|
0
|
353 }
|
|
354
|
|
355 if (cl->buf->in_file) {
|
|
356 cl->buf->file_pos += sent;
|
|
357 }
|
|
358
|
|
359 break;
|
|
360 }
|
|
361
|
|
362 if (eintr) {
|
126
|
363 continue;
|
0
|
364 }
|
|
365
|
126
|
366 if (!complete) {
|
0
|
367 wev->ready = 0;
|
|
368 return cl;
|
|
369 }
|
|
370
|
|
371 if (send >= limit || cl == NULL) {
|
|
372 return cl;
|
|
373 }
|
|
374
|
|
375 in = cl;
|
|
376 }
|
|
377 }
|