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 */