Annotation of embedaddon/nginx/src/os/unix/ngx_freebsd_sendfile_chain.c, revision 1.1.1.1
1.1 misho 1:
2: /*
3: * Copyright (C) Igor Sysoev
4: * Copyright (C) Nginx, Inc.
5: */
6:
7:
8: #include <ngx_config.h>
9: #include <ngx_core.h>
10: #include <ngx_event.h>
11:
12:
13: /*
14: * Although FreeBSD sendfile() allows to pass a header and a trailer,
15: * it cannot send a header with a part of the file in one packet until
16: * FreeBSD 5.3. Besides, over the fast ethernet connection sendfile()
17: * may send the partially filled packets, i.e. the 8 file pages may be sent
18: * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
19: * and then again the 11 full 1460-bytes packets.
20: *
21: * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)
22: * to postpone the sending - it not only sends a header and the first part of
23: * the file in one packet, but also sends the file pages in the full packets.
24: *
25: * But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending
26: * data that less than MSS, so that data may be sent with 5 second delay.
27: * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used
28: * for non-keepalive HTTP connections.
29: */
30:
31:
32: #if (IOV_MAX > 64)
33: #define NGX_HEADERS 64
34: #define NGX_TRAILERS 64
35: #else
36: #define NGX_HEADERS IOV_MAX
37: #define NGX_TRAILERS IOV_MAX
38: #endif
39:
40:
41: ngx_chain_t *
42: ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
43: {
44: int rc, flags;
45: u_char *prev;
46: off_t size, send, prev_send, aligned, sent, fprev;
47: size_t header_size, file_size;
48: ngx_uint_t eintr, eagain, complete;
49: ngx_err_t err;
50: ngx_buf_t *file;
51: ngx_array_t header, trailer;
52: ngx_event_t *wev;
53: ngx_chain_t *cl;
54: struct sf_hdtr hdtr;
55: struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
56:
57: wev = c->write;
58:
59: if (!wev->ready) {
60: return in;
61: }
62:
63: #if (NGX_HAVE_KQUEUE)
64:
65: if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
66: (void) ngx_connection_error(c, wev->kq_errno,
67: "kevent() reported about an closed connection");
68: wev->error = 1;
69: return NGX_CHAIN_ERROR;
70: }
71:
72: #endif
73:
74: /* the maximum limit size is the maximum size_t value - the page size */
75:
76: if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
77: limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
78: }
79:
80: send = 0;
81: eagain = 0;
82: flags = 0;
83:
84: header.elts = headers;
85: header.size = sizeof(struct iovec);
86: header.nalloc = NGX_HEADERS;
87: header.pool = c->pool;
88:
89: trailer.elts = trailers;
90: trailer.size = sizeof(struct iovec);
91: trailer.nalloc = NGX_TRAILERS;
92: trailer.pool = c->pool;
93:
94: for ( ;; ) {
95: file = NULL;
96: file_size = 0;
97: header_size = 0;
98: eintr = 0;
99: complete = 0;
100: prev_send = send;
101:
102: header.nelts = 0;
103: trailer.nelts = 0;
104:
105: /* create the header iovec and coalesce the neighbouring bufs */
106:
107: prev = NULL;
108: iov = NULL;
109:
110: for (cl = in; cl && send < limit; cl = cl->next) {
111:
112: if (ngx_buf_special(cl->buf)) {
113: continue;
114: }
115:
116: if (!ngx_buf_in_memory_only(cl->buf)) {
117: break;
118: }
119:
120: size = cl->buf->last - cl->buf->pos;
121:
122: if (send + size > limit) {
123: size = limit - send;
124: }
125:
126: if (prev == cl->buf->pos) {
127: iov->iov_len += (size_t) size;
128:
129: } else {
130: if (header.nelts >= IOV_MAX){
131: break;
132: }
133:
134: iov = ngx_array_push(&header);
135: if (iov == NULL) {
136: return NGX_CHAIN_ERROR;
137: }
138:
139: iov->iov_base = (void *) cl->buf->pos;
140: iov->iov_len = (size_t) size;
141: }
142:
143: prev = cl->buf->pos + (size_t) size;
144: header_size += (size_t) size;
145: send += size;
146: }
147:
148:
149: if (cl && cl->buf->in_file && send < limit) {
150: file = cl->buf;
151:
152: /* coalesce the neighbouring file bufs */
153:
154: do {
155: size = cl->buf->file_last - cl->buf->file_pos;
156:
157: if (send + size > limit) {
158: size = limit - send;
159:
160: aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
161: & ~((off_t) ngx_pagesize - 1);
162:
163: if (aligned <= cl->buf->file_last) {
164: size = aligned - cl->buf->file_pos;
165: }
166: }
167:
168: file_size += (size_t) size;
169: send += size;
170: fprev = cl->buf->file_pos + size;
171: cl = cl->next;
172:
173: } while (cl
174: && cl->buf->in_file
175: && send < limit
176: && file->file->fd == cl->buf->file->fd
177: && fprev == cl->buf->file_pos);
178: }
179:
180:
181: if (file) {
182:
183: /* create the trailer iovec and coalesce the neighbouring bufs */
184:
185: prev = NULL;
186: iov = NULL;
187:
188: while (cl && send < limit) {
189:
190: if (ngx_buf_special(cl->buf)) {
191: cl = cl->next;
192: continue;
193: }
194:
195: if (!ngx_buf_in_memory_only(cl->buf)) {
196: break;
197: }
198:
199: size = cl->buf->last - cl->buf->pos;
200:
201: if (send + size > limit) {
202: size = limit - send;
203: }
204:
205: if (prev == cl->buf->pos) {
206: iov->iov_len += (size_t) size;
207:
208: } else {
209: if (trailer.nelts >= IOV_MAX){
210: break;
211: }
212:
213: iov = ngx_array_push(&trailer);
214: if (iov == NULL) {
215: return NGX_CHAIN_ERROR;
216: }
217:
218: iov->iov_base = (void *) cl->buf->pos;
219: iov->iov_len = (size_t) size;
220: }
221:
222: prev = cl->buf->pos + (size_t) size;
223: send += size;
224: cl = cl->next;
225: }
226: }
227:
228: if (file) {
229:
230: if (ngx_freebsd_use_tcp_nopush
231: && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)
232: {
233: if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
234: err = ngx_errno;
235:
236: /*
237: * there is a tiny chance to be interrupted, however,
238: * we continue a processing without the TCP_NOPUSH
239: */
240:
241: if (err != NGX_EINTR) {
242: wev->error = 1;
243: (void) ngx_connection_error(c, err,
244: ngx_tcp_nopush_n " failed");
245: return NGX_CHAIN_ERROR;
246: }
247:
248: } else {
249: c->tcp_nopush = NGX_TCP_NOPUSH_SET;
250:
251: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
252: "tcp_nopush");
253: }
254: }
255:
256: /*
257: * sendfile() does unneeded work if sf_hdtr's count is 0,
258: * but corresponding pointer is not NULL
259: */
260:
261: hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
262: hdtr.hdr_cnt = header.nelts;
263: hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
264: hdtr.trl_cnt = trailer.nelts;
265:
266: /*
267: * the "nbytes bug" of the old sendfile() syscall:
268: * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
269: */
270:
271: if (!ngx_freebsd_sendfile_nbytes_bug) {
272: header_size = 0;
273: }
274:
275: sent = 0;
276:
277: #if (NGX_HAVE_AIO_SENDFILE)
278: flags = c->aio_sendfile ? SF_NODISKIO : 0;
279: #endif
280:
281: rc = sendfile(file->file->fd, c->fd, file->file_pos,
282: file_size + header_size, &hdtr, &sent, flags);
283:
284: if (rc == -1) {
285: err = ngx_errno;
286:
287: switch (err) {
288: case NGX_EAGAIN:
289: eagain = 1;
290: break;
291:
292: case NGX_EINTR:
293: eintr = 1;
294: break;
295:
296: #if (NGX_HAVE_AIO_SENDFILE)
297: case NGX_EBUSY:
298: c->busy_sendfile = file;
299: break;
300: #endif
301:
302: default:
303: wev->error = 1;
304: (void) ngx_connection_error(c, err, "sendfile() failed");
305: return NGX_CHAIN_ERROR;
306: }
307:
308: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
309: "sendfile() sent only %O bytes", sent);
310:
311: /*
312: * sendfile() in FreeBSD 3.x-4.x may return value >= 0
313: * on success, although only 0 is documented
314: */
315:
316: } else if (rc >= 0 && sent == 0) {
317:
318: /*
319: * if rc is OK and sent equal to zero, then someone
320: * has truncated the file, so the offset became beyond
321: * the end of the file
322: */
323:
324: ngx_log_error(NGX_LOG_ALERT, c->log, 0,
325: "sendfile() reported that \"%s\" was truncated at %O",
326: file->file->name.data, file->file_pos);
327:
328: return NGX_CHAIN_ERROR;
329: }
330:
331: ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
332: "sendfile: %d, @%O %O:%uz",
333: rc, file->file_pos, sent, file_size + header_size);
334:
335: } else {
336: rc = writev(c->fd, header.elts, header.nelts);
337:
338: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
339: "writev: %d of %uz", rc, header_size);
340:
341: if (rc == -1) {
342: err = ngx_errno;
343:
344: switch (err) {
345: case NGX_EAGAIN:
346: break;
347:
348: case NGX_EINTR:
349: eintr = 1;
350: break;
351:
352: default:
353: wev->error = 1;
354: ngx_connection_error(c, err, "writev() failed");
355: return NGX_CHAIN_ERROR;
356: }
357:
358: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
359: "writev() not ready");
360: }
361:
362: sent = rc > 0 ? rc : 0;
363: }
364:
365: if (send - prev_send == sent) {
366: complete = 1;
367: }
368:
369: c->sent += sent;
370:
371: for (cl = in; cl; cl = cl->next) {
372:
373: if (ngx_buf_special(cl->buf)) {
374: continue;
375: }
376:
377: if (sent == 0) {
378: break;
379: }
380:
381: size = ngx_buf_size(cl->buf);
382:
383: if (sent >= size) {
384: sent -= size;
385:
386: if (ngx_buf_in_memory(cl->buf)) {
387: cl->buf->pos = cl->buf->last;
388: }
389:
390: if (cl->buf->in_file) {
391: cl->buf->file_pos = cl->buf->file_last;
392: }
393:
394: continue;
395: }
396:
397: if (ngx_buf_in_memory(cl->buf)) {
398: cl->buf->pos += (size_t) sent;
399: }
400:
401: if (cl->buf->in_file) {
402: cl->buf->file_pos += sent;
403: }
404:
405: break;
406: }
407:
408: #if (NGX_HAVE_AIO_SENDFILE)
409: if (c->busy_sendfile) {
410: return cl;
411: }
412: #endif
413:
414: if (eagain) {
415:
416: /*
417: * sendfile() may return EAGAIN, even if it has sent a whole file
418: * part, it indicates that the successive sendfile() call would
419: * return EAGAIN right away and would not send anything.
420: * We use it as a hint.
421: */
422:
423: wev->ready = 0;
424: return cl;
425: }
426:
427: if (eintr) {
428: continue;
429: }
430:
431: if (!complete) {
432: wev->ready = 0;
433: return cl;
434: }
435:
436: if (send >= limit || cl == NULL) {
437: return cl;
438: }
439:
440: in = cl;
441: }
442: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>