Mercurial > hg > nginx
comparison src/os/unix/ngx_linux_sendfile_chain.c @ 6023:b550563ef96e
Added support for offloading Linux sendfile() in thread pools.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Sat, 14 Mar 2015 17:37:30 +0300 |
parents | c901f2764c27 |
children | 768e287a6f36 |
comparison
equal
deleted
inserted
replaced
6022:1fdba317ee6d | 6023:b550563ef96e |
---|---|
10 #include <ngx_event.h> | 10 #include <ngx_event.h> |
11 | 11 |
12 | 12 |
13 static ssize_t ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, | 13 static ssize_t ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, |
14 size_t size); | 14 size_t size); |
15 | |
16 #if (NGX_THREADS) | |
17 #include <ngx_thread_pool.h> | |
18 | |
19 #if !(NGX_HAVE_SENDFILE64) | |
20 #error sendfile64() is required! | |
21 #endif | |
22 | |
23 static ngx_int_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, | |
24 size_t size, size_t *sent); | |
25 static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log); | |
26 #endif | |
15 | 27 |
16 | 28 |
17 /* | 29 /* |
18 * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit | 30 * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit |
19 * offsets only, and the including <sys/sendfile.h> breaks the compiling, | 31 * offsets only, and the including <sys/sendfile.h> breaks the compiling, |
33 | 45 |
34 ngx_chain_t * | 46 ngx_chain_t * |
35 ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) | 47 ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) |
36 { | 48 { |
37 int tcp_nodelay; | 49 int tcp_nodelay; |
38 off_t send, prev_send, sent; | 50 off_t send, prev_send; |
39 size_t file_size; | 51 size_t file_size, sent; |
40 ssize_t n; | 52 ssize_t n; |
41 ngx_err_t err; | 53 ngx_err_t err; |
42 ngx_buf_t *file; | 54 ngx_buf_t *file; |
43 ngx_event_t *wev; | 55 ngx_event_t *wev; |
44 ngx_chain_t *cl; | 56 ngx_chain_t *cl; |
45 ngx_iovec_t header; | 57 ngx_iovec_t header; |
46 struct iovec headers[NGX_IOVS_PREALLOCATE]; | 58 struct iovec headers[NGX_IOVS_PREALLOCATE]; |
59 #if (NGX_THREADS) | |
60 ngx_int_t rc; | |
61 ngx_uint_t thread_handled, thread_complete; | |
62 #endif | |
47 | 63 |
48 wev = c->write; | 64 wev = c->write; |
49 | 65 |
50 if (!wev->ready) { | 66 if (!wev->ready) { |
51 return in; | 67 return in; |
64 header.iovs = headers; | 80 header.iovs = headers; |
65 header.nalloc = NGX_IOVS_PREALLOCATE; | 81 header.nalloc = NGX_IOVS_PREALLOCATE; |
66 | 82 |
67 for ( ;; ) { | 83 for ( ;; ) { |
68 prev_send = send; | 84 prev_send = send; |
85 #if (NGX_THREADS) | |
86 thread_handled = 0; | |
87 thread_complete = 0; | |
88 #endif | |
69 | 89 |
70 /* create the iovec and coalesce the neighbouring bufs */ | 90 /* create the iovec and coalesce the neighbouring bufs */ |
71 | 91 |
72 cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); | 92 cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); |
73 | 93 |
156 if (file_size == 0) { | 176 if (file_size == 0) { |
157 ngx_debug_point(); | 177 ngx_debug_point(); |
158 return NGX_CHAIN_ERROR; | 178 return NGX_CHAIN_ERROR; |
159 } | 179 } |
160 #endif | 180 #endif |
161 n = ngx_linux_sendfile(c, file, file_size); | 181 |
182 #if (NGX_THREADS) | |
183 if (file->file->thread_handler) { | |
184 rc = ngx_linux_sendfile_thread(c, file, file_size, &sent); | |
185 | |
186 switch (rc) { | |
187 case NGX_OK: | |
188 thread_handled = 1; | |
189 break; | |
190 | |
191 case NGX_DONE: | |
192 thread_complete = 1; | |
193 break; | |
194 | |
195 case NGX_AGAIN: | |
196 break; | |
197 | |
198 default: /* NGX_ERROR */ | |
199 return NGX_CHAIN_ERROR; | |
200 } | |
201 | |
202 } else | |
203 #endif | |
204 { | |
205 n = ngx_linux_sendfile(c, file, file_size); | |
206 | |
207 if (n == NGX_ERROR) { | |
208 return NGX_CHAIN_ERROR; | |
209 } | |
210 | |
211 sent = (n == NGX_AGAIN) ? 0 : n; | |
212 } | |
213 | |
214 } else { | |
215 n = ngx_writev(c, &header); | |
162 | 216 |
163 if (n == NGX_ERROR) { | 217 if (n == NGX_ERROR) { |
164 return NGX_CHAIN_ERROR; | 218 return NGX_CHAIN_ERROR; |
165 } | 219 } |
166 | 220 |
167 sent = (n == NGX_AGAIN) ? 0 : n; | 221 sent = (n == NGX_AGAIN) ? 0 : n; |
168 | |
169 } else { | |
170 n = ngx_writev(c, &header); | |
171 | |
172 if (n == NGX_ERROR) { | |
173 return NGX_CHAIN_ERROR; | |
174 } | |
175 | |
176 sent = (n == NGX_AGAIN) ? 0 : n; | |
177 } | 222 } |
178 | 223 |
179 c->sent += sent; | 224 c->sent += sent; |
180 | 225 |
181 in = ngx_chain_update_sent(in, sent); | 226 in = ngx_chain_update_sent(in, sent); |
182 | 227 |
183 if (send - prev_send != sent) { | 228 if ((size_t) (send - prev_send) != sent) { |
229 #if (NGX_THREADS) | |
230 if (thread_handled) { | |
231 return in; | |
232 } | |
233 | |
234 if (thread_complete) { | |
235 send = prev_send + sent; | |
236 continue; | |
237 } | |
238 #endif | |
184 wev->ready = 0; | 239 wev->ready = 0; |
185 return in; | 240 return in; |
186 } | 241 } |
187 | 242 |
188 if (send >= limit || in == NULL) { | 243 if (send >= limit || in == NULL) { |
240 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %z of %uz @%O", | 295 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %z of %uz @%O", |
241 n, size, file->file_pos); | 296 n, size, file->file_pos); |
242 | 297 |
243 return n; | 298 return n; |
244 } | 299 } |
300 | |
301 | |
302 #if (NGX_THREADS) | |
303 | |
304 typedef struct { | |
305 ngx_buf_t *file; | |
306 ngx_socket_t socket; | |
307 size_t size; | |
308 | |
309 size_t sent; | |
310 ngx_err_t err; | |
311 } ngx_linux_sendfile_ctx_t; | |
312 | |
313 | |
314 static ngx_int_t | |
315 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, | |
316 size_t *sent) | |
317 { | |
318 ngx_uint_t flags; | |
319 ngx_event_t *wev; | |
320 ngx_thread_task_t *task; | |
321 ngx_linux_sendfile_ctx_t *ctx; | |
322 | |
323 ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, | |
324 "linux sendfile thread: %d, %uz, %O", | |
325 file->file->fd, size, file->file_pos); | |
326 | |
327 task = c->sendfile_task; | |
328 | |
329 if (task == NULL) { | |
330 task = ngx_thread_task_alloc(c->pool, sizeof(ngx_linux_sendfile_ctx_t)); | |
331 if (task == NULL) { | |
332 return NGX_ERROR; | |
333 } | |
334 | |
335 task->handler = ngx_linux_sendfile_thread_handler; | |
336 | |
337 c->sendfile_task = task; | |
338 } | |
339 | |
340 ctx = task->ctx; | |
341 wev = c->write; | |
342 | |
343 if (task->event.complete) { | |
344 task->event.complete = 0; | |
345 | |
346 if (ctx->err && ctx->err != NGX_EAGAIN) { | |
347 wev->error = 1; | |
348 ngx_connection_error(c, ctx->err, "sendfile() failed"); | |
349 return NGX_ERROR; | |
350 } | |
351 | |
352 *sent = ctx->sent; | |
353 | |
354 return (ctx->sent == ctx->size) ? NGX_DONE : NGX_AGAIN; | |
355 } | |
356 | |
357 ctx->file = file; | |
358 ctx->socket = c->fd; | |
359 ctx->size = size; | |
360 | |
361 if (wev->active) { | |
362 flags = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? NGX_CLEAR_EVENT | |
363 : NGX_LEVEL_EVENT; | |
364 | |
365 if (ngx_del_event(wev, NGX_WRITE_EVENT, flags) == NGX_ERROR) { | |
366 return NGX_ERROR; | |
367 } | |
368 } | |
369 | |
370 if (file->file->thread_handler(task, file->file) != NGX_OK) { | |
371 return NGX_ERROR; | |
372 } | |
373 | |
374 *sent = 0; | |
375 | |
376 return NGX_OK; | |
377 } | |
378 | |
379 | |
380 static void | |
381 ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log) | |
382 { | |
383 ngx_linux_sendfile_ctx_t *ctx = data; | |
384 | |
385 off_t offset; | |
386 ssize_t n; | |
387 ngx_buf_t *file; | |
388 | |
389 ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "linux sendfile thread handler"); | |
390 | |
391 file = ctx->file; | |
392 offset = file->file_pos; | |
393 | |
394 again: | |
395 | |
396 n = sendfile(ctx->socket, file->file->fd, &offset, ctx->size); | |
397 | |
398 if (n == -1) { | |
399 ctx->err = ngx_errno; | |
400 | |
401 } else { | |
402 ctx->sent = n; | |
403 ctx->err = 0; | |
404 } | |
405 | |
406 #if 0 | |
407 ngx_time_update(); | |
408 #endif | |
409 | |
410 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0, | |
411 "sendfile: %z (err: %i) of %uz @%O", | |
412 n, ctx->err, ctx->size, file->file_pos); | |
413 | |
414 if (ctx->err == NGX_EINTR) { | |
415 goto again; | |
416 } | |
417 } | |
418 | |
419 #endif /* NGX_THREADS */ |