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