Mercurial > hg > nginx
comparison src/os/unix/ngx_freebsd_sendfile_chain.c @ 96:a23d010f356d
nginx-0.0.1-2003-05-27-16:18:54 import
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 27 May 2003 12:18:54 +0000 |
parents | 8220378432a8 |
children | a059e1aa65d4 |
comparison
equal
deleted
inserted
replaced
95:b48066122884 | 96:a23d010f356d |
---|---|
1 | 1 |
2 #include <ngx_config.h> | 2 #include <ngx_config.h> |
3 | |
4 #include <ngx_core.h> | 3 #include <ngx_core.h> |
5 #include <ngx_types.h> | |
6 #include <ngx_alloc.h> | |
7 #include <ngx_array.h> | |
8 #include <ngx_hunk.h> | |
9 #include <ngx_connection.h> | |
10 #include <ngx_sendv.h> | |
11 #include <ngx_sendfile.h> | |
12 #include <ngx_freebsd_init.h> | 4 #include <ngx_freebsd_init.h> |
5 | |
6 | |
7 /* | |
8 sendfile() often sends 4K pages over ethernet in 3 packets: 2x1460 and 1176 | |
9 or in 6 packets: 5x1460 and 892. Besides although sendfile() allows | |
10 to pass the header and the trailer it never sends the header or the trailer | |
11 with the part of the file in one packet. So we use TCP_NOPUSH (similar | |
12 to Linux's TCP_CORK) to postpone the sending - it not only sends the header | |
13 and the first part of the file in one packet but also sends 4K pages | |
14 in the full packets. | |
15 | |
16 The turning TCP_NOPUSH off flushes any pending data at least in FreeBSD 4.2, | |
17 although there's special fix in src/sys/netinet/tcp_usrreq.c just before | |
18 FreeBSD 4.5. | |
19 */ | |
13 | 20 |
14 | 21 |
15 ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in) | 22 ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in) |
16 { | 23 { |
17 int rc; | 24 int rc, eintr, tcp_nopush; |
18 char *prev; | 25 char *prev; |
19 size_t hsize, size; | 26 size_t hsize, size; |
20 off_t sent; | 27 off_t sent; |
21 struct iovec *iov; | 28 struct iovec *iov; |
22 struct sf_hdtr hdtr; | 29 struct sf_hdtr hdtr; |
23 ngx_err_t err; | 30 ngx_err_t err; |
24 ngx_array_t header, trailer; | 31 ngx_array_t header, trailer; |
25 ngx_hunk_t *file; | 32 ngx_hunk_t *file; |
26 ngx_chain_t *ce; | 33 ngx_chain_t *ce, *tail; |
27 | 34 |
28 ce = in; | 35 tcp_nopush = 0; |
29 file = NULL; | 36 |
30 hsize = 0; | 37 do { |
31 | 38 ce = in; |
32 ngx_init_array(header, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR); | 39 file = NULL; |
33 ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR); | 40 hsize = 0; |
34 | 41 eintr = 0; |
35 /* create the header iovec */ | 42 |
36 if (ngx_hunk_in_memory_only(ce->hunk)) { | 43 ngx_init_array(header, c->pool, 10, sizeof(struct iovec), |
37 prev = NULL; | 44 NGX_CHAIN_ERROR); |
38 iov = NULL; | 45 ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec), |
39 | 46 NGX_CHAIN_ERROR); |
40 /* create the iovec and coalesce the neighbouring chain entries */ | 47 |
41 while (ce && ngx_hunk_in_memory_only(ce->hunk)) { | 48 /* create the header iovec */ |
42 | 49 if (ngx_hunk_in_memory_only(ce->hunk)) { |
43 if (prev == ce->hunk->pos) { | 50 prev = NULL; |
44 iov->iov_len += ce->hunk->last - ce->hunk->pos; | 51 iov = NULL; |
45 prev = ce->hunk->last; | 52 |
46 | 53 /* create the iovec and coalesce the neighbouring chain entries */ |
54 while (ce && ngx_hunk_in_memory_only(ce->hunk)) { | |
55 | |
56 if (prev == ce->hunk->pos) { | |
57 iov->iov_len += ce->hunk->last - ce->hunk->pos; | |
58 prev = ce->hunk->last; | |
59 | |
60 } else { | |
61 ngx_test_null(iov, ngx_push_array(&header), | |
62 NGX_CHAIN_ERROR); | |
63 iov->iov_base = ce->hunk->pos; | |
64 iov->iov_len = ce->hunk->last - ce->hunk->pos; | |
65 prev = ce->hunk->last; | |
66 } | |
67 | |
68 hsize += ce->hunk->last - ce->hunk->pos; | |
69 | |
70 ce = ce->next; | |
71 } | |
72 } | |
73 | |
74 /* TODO: coalesce the neighbouring file hunks */ | |
75 if (ce && (ce->hunk->type & NGX_HUNK_FILE)) { | |
76 file = ce->hunk; | |
77 ce = ce->next; | |
78 } | |
79 | |
80 /* create the trailer iovec */ | |
81 if (ce && ngx_hunk_in_memory_only(ce->hunk)) { | |
82 prev = NULL; | |
83 iov = NULL; | |
84 | |
85 /* create the iovec and coalesce the neighbouring chain entries */ | |
86 while (ce && ngx_hunk_in_memory_only(ce->hunk)) { | |
87 | |
88 if (prev == ce->hunk->pos) { | |
89 iov->iov_len += ce->hunk->last - ce->hunk->pos; | |
90 prev = ce->hunk->last; | |
91 | |
92 } else { | |
93 ngx_test_null(iov, ngx_push_array(&trailer), | |
94 NGX_CHAIN_ERROR); | |
95 iov->iov_base = ce->hunk->pos; | |
96 iov->iov_len = ce->hunk->last - ce->hunk->pos; | |
97 prev = ce->hunk->last; | |
98 } | |
99 | |
100 ce = ce->next; | |
101 } | |
102 } | |
103 | |
104 tail = ce; | |
105 | |
106 if (file) { | |
107 | |
108 if (tcp_nopush == 0) { | |
109 tcp_nopush = 1; | |
110 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NOPUSH, | |
111 (const void *) &tcp_nopush, | |
112 sizeof(int)) == -1) | |
113 { | |
114 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, | |
115 "setsockopt(TCP_NO_PUSH) failed"); | |
116 return NGX_CHAIN_ERROR; | |
117 } | |
118 } | |
119 | |
120 hdtr.headers = (struct iovec *) header.elts; | |
121 hdtr.hdr_cnt = header.nelts; | |
122 hdtr.trailers = (struct iovec *) trailer.elts; | |
123 hdtr.trl_cnt = trailer.nelts; | |
124 | |
125 if (ngx_freebsd_sendfile_nbytes_bug == 0) { | |
126 hsize = 0; | |
127 } | |
128 | |
129 rc = sendfile(file->file->fd, c->fd, file->file_pos, | |
130 (size_t) (file->file_last - file->file_pos) + hsize, | |
131 &hdtr, &sent, 0); | |
132 | |
133 if (rc == -1) { | |
134 err = ngx_errno; | |
135 | |
136 if (err == NGX_EINTR) { | |
137 eintr = 1; | |
138 } | |
139 | |
140 if (err == NGX_EAGAIN || err == NGX_EINTR) { | |
141 ngx_log_error(NGX_LOG_INFO, c->log, err, | |
142 "sendfile() sent only %qd bytes", sent); | |
143 | |
144 } else { | |
145 ngx_log_error(NGX_LOG_CRIT, c->log, err, | |
146 "sendfile() failed"); | |
147 return NGX_CHAIN_ERROR; | |
148 } | |
149 } | |
150 | |
151 #if (NGX_DEBUG_WRITE_CHAIN) | |
152 ngx_log_debug(c->log, "sendfile: %d, @%qd %qd:%d" _ | |
153 rc _ file->file_pos _ sent _ | |
154 (size_t) (file->file_last - file->file_pos) + hsize); | |
155 #endif | |
156 | |
157 } else { | |
158 rc = writev(c->fd, (struct iovec *) header.elts, header.nelts); | |
159 | |
160 if (rc == -1) { | |
161 err = ngx_errno; | |
162 if (err == NGX_EAGAIN) { | |
163 ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN"); | |
164 | |
165 } else if (err == NGX_EINTR) { | |
166 eintr = 1; | |
167 ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR"); | |
168 | |
169 } else { | |
170 ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed"); | |
171 return NGX_CHAIN_ERROR; | |
172 } | |
173 } | |
174 | |
175 sent = rc > 0 ? rc : 0; | |
176 | |
177 #if (NGX_DEBUG_WRITE_CHAIN) | |
178 ngx_log_debug(c->log, "writev: %qd" _ sent); | |
179 #endif | |
180 } | |
181 | |
182 c->sent += sent; | |
183 | |
184 for (ce = in; ce && sent > 0; ce = ce->next) { | |
185 | |
186 if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { | |
187 size = ce->hunk->last - ce->hunk->pos; | |
47 } else { | 188 } else { |
48 ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR); | 189 size = ce->hunk->file_last - ce->hunk->file_pos; |
49 iov->iov_base = ce->hunk->pos; | 190 } |
50 iov->iov_len = ce->hunk->last - ce->hunk->pos; | 191 |
51 prev = ce->hunk->last; | 192 if (sent >= size) { |
52 } | 193 sent -= size; |
53 | 194 |
54 if (ngx_freebsd_sendfile_nbytes_bug) { | 195 if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { |
55 hsize += ce->hunk->last - ce->hunk->pos; | 196 ce->hunk->pos = ce->hunk->last; |
56 } | 197 } |
57 | 198 |
58 ce = ce->next; | 199 if (ce->hunk->type & NGX_HUNK_FILE) { |
200 ce->hunk->file_pos = ce->hunk->file_last; | |
201 } | |
202 | |
203 continue; | |
204 } | |
205 | |
206 if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { | |
207 ce->hunk->pos += sent; | |
208 } | |
209 | |
210 if (ce->hunk->type & NGX_HUNK_FILE) { | |
211 ce->hunk->file_pos += sent; | |
212 } | |
213 | |
214 break; | |
215 } | |
216 | |
217 ngx_destroy_array(&trailer); | |
218 ngx_destroy_array(&header); | |
219 | |
220 in = ce; | |
221 | |
222 } while ((tail && tail == ce) || eintr); | |
223 | |
224 if (tcp_nopush == 1) { | |
225 tcp_nopush = 0; | |
226 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NOPUSH, | |
227 (const void *) &tcp_nopush, | |
228 sizeof(int)) == -1) | |
229 { | |
230 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, | |
231 "setsockopt(!TCP_NO_PUSH) failed"); | |
232 return NGX_CHAIN_ERROR; | |
59 } | 233 } |
60 } | 234 } |
61 | |
62 /* TODO: coalesce the neighbouring file hunks */ | |
63 if (ce && (ce->hunk->type & NGX_HUNK_FILE)) { | |
64 file = ce->hunk; | |
65 ce = ce->next; | |
66 } | |
67 | |
68 /* create the trailer iovec */ | |
69 if (ce && ngx_hunk_in_memory_only(ce->hunk)) { | |
70 prev = NULL; | |
71 iov = NULL; | |
72 | |
73 /* create the iovec and coalesce the neighbouring chain entries */ | |
74 while (ce && ngx_hunk_in_memory_only(ce->hunk)) { | |
75 | |
76 if (prev == ce->hunk->pos) { | |
77 iov->iov_len += ce->hunk->last - ce->hunk->pos; | |
78 prev = ce->hunk->last; | |
79 | |
80 } else { | |
81 ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR); | |
82 iov->iov_base = ce->hunk->pos; | |
83 iov->iov_len = ce->hunk->last - ce->hunk->pos; | |
84 prev = ce->hunk->last; | |
85 } | |
86 | |
87 ce = ce->next; | |
88 } | |
89 } | |
90 | |
91 if (file) { | |
92 hdtr.headers = (struct iovec *) header.elts; | |
93 hdtr.hdr_cnt = header.nelts; | |
94 hdtr.trailers = (struct iovec *) trailer.elts; | |
95 hdtr.trl_cnt = trailer.nelts; | |
96 | |
97 rc = sendfile(file->file->fd, c->fd, file->file_pos, | |
98 (size_t) (file->file_last - file->file_pos) + hsize, | |
99 &hdtr, &sent, 0); | |
100 | |
101 if (rc == -1) { | |
102 err = ngx_errno; | |
103 if (err == NGX_EAGAIN || err == NGX_EINTR) { | |
104 ngx_log_error(NGX_LOG_INFO, c->log, err, | |
105 "sendfile() sent only %qd bytes", sent); | |
106 | |
107 } else { | |
108 ngx_log_error(NGX_LOG_CRIT, c->log, err, "sendfile() failed"); | |
109 return NGX_CHAIN_ERROR; | |
110 } | |
111 } | |
112 | |
113 #if (NGX_DEBUG_WRITE_CHAIN) | |
114 ngx_log_debug(c->log, "sendfile: %d, @%qd %qd:%d" _ | |
115 rc _ file->file_pos _ sent _ | |
116 (size_t) (file->file_last - file->file_pos) + hsize); | |
117 #endif | |
118 | |
119 } else { | |
120 rc = writev(c->fd, (struct iovec *) header.elts, header.nelts); | |
121 | |
122 if (rc == -1) { | |
123 err = ngx_errno; | |
124 if (err == NGX_EAGAIN) { | |
125 ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN"); | |
126 | |
127 } else if (err == NGX_EINTR) { | |
128 ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR"); | |
129 | |
130 } else { | |
131 ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed"); | |
132 return NGX_CHAIN_ERROR; | |
133 } | |
134 } | |
135 | |
136 sent = rc > 0 ? rc : 0; | |
137 } | |
138 | |
139 #if (NGX_DEBUG_WRITE_CHAIN) | |
140 ngx_log_debug(c->log, "sendv: %qd" _ sent); | |
141 #endif | |
142 | |
143 c->sent += sent; | |
144 | |
145 for (ce = in; ce && sent > 0; ce = ce->next) { | |
146 | |
147 if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { | |
148 size = ce->hunk->last - ce->hunk->pos; | |
149 } else { | |
150 size = ce->hunk->file_last - ce->hunk->file_pos; | |
151 } | |
152 | |
153 if (sent >= size) { | |
154 sent -= size; | |
155 | |
156 if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { | |
157 ce->hunk->pos = ce->hunk->last; | |
158 } | |
159 | |
160 if (ce->hunk->type & NGX_HUNK_FILE) { | |
161 ce->hunk->file_pos = ce->hunk->file_last; | |
162 } | |
163 | |
164 continue; | |
165 } | |
166 | |
167 if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { | |
168 ce->hunk->pos += sent; | |
169 } | |
170 | |
171 if (ce->hunk->type & NGX_HUNK_FILE) { | |
172 ce->hunk->file_pos += sent; | |
173 } | |
174 | |
175 break; | |
176 } | |
177 | |
178 ngx_destroy_array(&trailer); | |
179 ngx_destroy_array(&header); | |
180 | 235 |
181 return ce; | 236 return ce; |
182 } | 237 } |