Annotation of embedaddon/lighttpd/src/network_linux_sendfile.c, revision 1.1.1.1
1.1 misho 1: #include "network_backends.h"
2:
3: #ifdef USE_LINUX_SENDFILE
4:
5: #include "network.h"
6: #include "fdevent.h"
7: #include "log.h"
8: #include "stat_cache.h"
9:
10: #include <sys/types.h>
11: #include <sys/socket.h>
12: #include <sys/stat.h>
13: #include <sys/time.h>
14: #include <sys/resource.h>
15:
16: #include <netinet/in.h>
17: #include <netinet/tcp.h>
18:
19: #include <errno.h>
20: #include <fcntl.h>
21: #include <unistd.h>
22: #include <netdb.h>
23: #include <string.h>
24: #include <stdlib.h>
25: #include <fcntl.h>
26:
27: /* on linux 2.4.29 + debian/ubuntu we have crashes if this is enabled */
28: #undef HAVE_POSIX_FADVISE
29:
30: int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
31: chunk *c;
32:
33: for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) {
34: int chunk_finished = 0;
35:
36: switch(c->type) {
37: case MEM_CHUNK: {
38: char * offset;
39: off_t toSend;
40: ssize_t r;
41:
42: size_t num_chunks, i;
43: struct iovec chunks[UIO_MAXIOV];
44: chunk *tc;
45: size_t num_bytes = 0;
46:
47: /* build writev list
48: *
49: * 1. limit: num_chunks < UIO_MAXIOV
50: * 2. limit: num_bytes < max_bytes
51: */
52: for (num_chunks = 0, tc = c;
53: tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV;
54: tc = tc->next, num_chunks++);
55:
56: for (tc = c, i = 0; i < num_chunks; tc = tc->next, i++) {
57: if (tc->mem->used == 0) {
58: chunks[i].iov_base = tc->mem->ptr;
59: chunks[i].iov_len = 0;
60: } else {
61: offset = tc->mem->ptr + tc->offset;
62: toSend = tc->mem->used - 1 - tc->offset;
63:
64: chunks[i].iov_base = offset;
65:
66: /* protect the return value of writev() */
67: if (toSend > max_bytes ||
68: (off_t) num_bytes + toSend > max_bytes) {
69: chunks[i].iov_len = max_bytes - num_bytes;
70:
71: num_chunks = i + 1;
72: break;
73: } else {
74: chunks[i].iov_len = toSend;
75: }
76:
77: num_bytes += toSend;
78: }
79: }
80:
81: if ((r = writev(fd, chunks, num_chunks)) < 0) {
82: switch (errno) {
83: case EAGAIN:
84: case EINTR:
85: r = 0;
86: break;
87: case EPIPE:
88: case ECONNRESET:
89: return -2;
90: default:
91: log_error_write(srv, __FILE__, __LINE__, "ssd",
92: "writev failed:", strerror(errno), fd);
93:
94: return -1;
95: }
96: }
97:
98: /* check which chunks have been written */
99: cq->bytes_out += r;
100: max_bytes -= r;
101:
102: for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) {
103: if (r >= (ssize_t)chunks[i].iov_len) {
104: /* written */
105: r -= chunks[i].iov_len;
106: tc->offset += chunks[i].iov_len;
107:
108: if (chunk_finished) {
109: /* skip the chunks from further touches */
110: c = c->next;
111: } else {
112: /* chunks_written + c = c->next is done in the for()*/
113: chunk_finished = 1;
114: }
115: } else {
116: /* partially written */
117:
118: tc->offset += r;
119: chunk_finished = 0;
120:
121: break;
122: }
123: }
124:
125: break;
126: }
127: case FILE_CHUNK: {
128: ssize_t r;
129: off_t offset;
130: off_t toSend;
131: stat_cache_entry *sce = NULL;
132:
133: offset = c->file.start + c->offset;
134: toSend = c->file.length - c->offset;
135: if (toSend > max_bytes) toSend = max_bytes;
136:
137: /* open file if not already opened */
138: if (-1 == c->file.fd) {
139: if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
140: log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
141:
142: return -1;
143: }
144: #ifdef FD_CLOEXEC
145: fcntl(c->file.fd, F_SETFD, FD_CLOEXEC);
146: #endif
147: #ifdef HAVE_POSIX_FADVISE
148: /* tell the kernel that we want to stream the file */
149: if (-1 == posix_fadvise(c->file.fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
150: if (ENOSYS != errno) {
151: log_error_write(srv, __FILE__, __LINE__, "ssd",
152: "posix_fadvise failed:", strerror(errno), c->file.fd);
153: }
154: }
155: #endif
156: }
157:
158: if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) {
159: switch (errno) {
160: case EAGAIN:
161: case EINTR:
162: /* ok, we can't send more, let's try later again */
163: r = 0;
164: break;
165: case EPIPE:
166: case ECONNRESET:
167: return -2;
168: default:
169: log_error_write(srv, __FILE__, __LINE__, "ssd",
170: "sendfile failed:", strerror(errno), fd);
171: return -1;
172: }
173: } else if (r == 0) {
174: int oerrno = errno;
175: /* We got an event to write but we wrote nothing
176: *
177: * - the file shrinked -> error
178: * - the remote side closed inbetween -> remote-close */
179:
180: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
181: /* file is gone ? */
182: return -1;
183: }
184:
185: if (offset > sce->st.st_size) {
186: /* file shrinked, close the connection */
187: errno = oerrno;
188:
189: return -1;
190: }
191:
192: errno = oerrno;
193: return -2;
194: }
195:
196: #ifdef HAVE_POSIX_FADVISE
197: #if 0
198: #define K * 1024
199: #define M * 1024 K
200: #define READ_AHEAD 4 M
201: /* check if we need a new chunk */
202: if ((c->offset & ~(READ_AHEAD - 1)) != ((c->offset + r) & ~(READ_AHEAD - 1))) {
203: /* tell the kernel that we want to stream the file */
204: if (-1 == posix_fadvise(c->file.fd, (c->offset + r) & ~(READ_AHEAD - 1), READ_AHEAD, POSIX_FADV_NOREUSE)) {
205: log_error_write(srv, __FILE__, __LINE__, "ssd",
206: "posix_fadvise failed:", strerror(errno), c->file.fd);
207: }
208: }
209: #endif
210: #endif
211:
212: c->offset += r;
213: cq->bytes_out += r;
214: max_bytes -= r;
215:
216: if (c->offset == c->file.length) {
217: chunk_finished = 1;
218:
219: /* chunk_free() / chunk_reset() will cleanup for us but it is a ok to be faster :) */
220:
221: if (c->file.fd != -1) {
222: close(c->file.fd);
223: c->file.fd = -1;
224: }
225: }
226:
227: break;
228: }
229: default:
230:
231: log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
232:
233: return -1;
234: }
235:
236: if (!chunk_finished) {
237: /* not finished yet */
238:
239: break;
240: }
241: }
242:
243: return 0;
244: }
245:
246: #endif
247: #if 0
248: network_linuxsendfile_init(void) {
249: p->write = network_linuxsendfile_write_chunkset;
250: }
251: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>