File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / network_linux_sendfile.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    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: 				fd_close_on_exec(c->file.fd);
  145: #ifdef HAVE_POSIX_FADVISE
  146: 				/* tell the kernel that we want to stream the file */
  147: 				if (-1 == posix_fadvise(c->file.fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
  148: 					if (ENOSYS != errno) {
  149: 						log_error_write(srv, __FILE__, __LINE__, "ssd",
  150: 							"posix_fadvise failed:", strerror(errno), c->file.fd);
  151: 					}
  152: 				}
  153: #endif
  154: 			}
  155: 
  156: 			if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) {
  157: 				switch (errno) {
  158: 				case EAGAIN:
  159: 				case EINTR:
  160: 					/* ok, we can't send more, let's try later again */
  161: 					r = 0;
  162: 					break;
  163: 				case EPIPE:
  164: 				case ECONNRESET:
  165: 					return -2;
  166: 				default:
  167: 					log_error_write(srv, __FILE__, __LINE__, "ssd",
  168: 							"sendfile failed:", strerror(errno), fd);
  169: 					return -1;
  170: 				}
  171: 			} else if (r == 0) {
  172: 				int oerrno = errno;
  173: 				/* We got an event to write but we wrote nothing
  174: 				 *
  175: 				 * - the file shrinked -> error
  176: 				 * - the remote side closed inbetween -> remote-close */
  177: 
  178: 				if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
  179: 					/* file is gone ? */
  180: 					return -1;
  181: 				}
  182: 
  183: 				if (offset > sce->st.st_size) {
  184: 					/* file shrinked, close the connection */
  185: 					errno = oerrno;
  186: 
  187: 					return -1;
  188: 				}
  189: 
  190: 				errno = oerrno;
  191: 				return -2;
  192: 			}
  193: 
  194: #ifdef HAVE_POSIX_FADVISE
  195: #if 0
  196: #define K * 1024
  197: #define M * 1024 K
  198: #define READ_AHEAD 4 M
  199: 			/* check if we need a new chunk */
  200: 			if ((c->offset & ~(READ_AHEAD - 1)) != ((c->offset + r) & ~(READ_AHEAD - 1))) {
  201: 				/* tell the kernel that we want to stream the file */
  202: 				if (-1 == posix_fadvise(c->file.fd, (c->offset + r) & ~(READ_AHEAD - 1), READ_AHEAD, POSIX_FADV_NOREUSE)) {
  203: 					log_error_write(srv, __FILE__, __LINE__, "ssd",
  204: 						"posix_fadvise failed:", strerror(errno), c->file.fd);
  205: 				}
  206: 			}
  207: #endif
  208: #endif
  209: 
  210: 			c->offset += r;
  211: 			cq->bytes_out += r;
  212: 			max_bytes -= r;
  213: 
  214: 			if (c->offset == c->file.length) {
  215: 				chunk_finished = 1;
  216: 
  217: 				/* chunk_free() / chunk_reset() will cleanup for us but it is a ok to be faster :) */
  218: 
  219: 				if (c->file.fd != -1) {
  220: 					close(c->file.fd);
  221: 					c->file.fd = -1;
  222: 				}
  223: 			}
  224: 
  225: 			break;
  226: 		}
  227: 		default:
  228: 
  229: 			log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
  230: 
  231: 			return -1;
  232: 		}
  233: 
  234: 		if (!chunk_finished) {
  235: 			/* not finished yet */
  236: 
  237: 			break;
  238: 		}
  239: 	}
  240: 
  241: 	return 0;
  242: }
  243: 
  244: #endif
  245: #if 0
  246: network_linuxsendfile_init(void) {
  247: 	p->write = network_linuxsendfile_write_chunkset;
  248: }
  249: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>