From 142971a80c47e319b1362bf46b85536391dce756 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Sun, 29 Oct 2017 22:36:49 -0400 Subject: [PATCH] [core] consolidate backend network write handlers network_write.[ch] isolates various write, writev, sendfile wrappers --- src/CMakeLists.txt | 7 - src/Makefile.am | 8 +- src/SConscript | 7 +- src/base.h | 2 +- src/connections.c | 2 +- src/gw_backend.c | 3 +- src/meson.build | 7 - src/network.c | 76 +--- src/network_backends.h | 119 ------ src/network_darwin_sendfile.c | 82 ---- src/network_freebsd_sendfile.c | 81 ---- src/network_linux_sendfile.c | 78 ---- src/network_solaris_sendfilev.c | 92 ----- src/network_write.c | 852 ++++++++++++++++++++++++++++++++++------ src/network_write.h | 9 + src/network_write_mmap.c | 210 ---------- src/network_write_no_mmap.c | 84 ---- src/network_writev.c | 121 ------ src/server.c | 44 +-- 19 files changed, 762 insertions(+), 1122 deletions(-) delete mode 100644 src/network_backends.h delete mode 100644 src/network_darwin_sendfile.c delete mode 100644 src/network_freebsd_sendfile.c delete mode 100644 src/network_linux_sendfile.c delete mode 100644 src/network_solaris_sendfilev.c rewrite src/network_write.c (90%) create mode 100644 src/network_write.h delete mode 100644 src/network_write_mmap.c delete mode 100644 src/network_write_no_mmap.c delete mode 100644 src/network_writev.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e7c5948..f285f836 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -590,14 +590,7 @@ add_executable(lighttpd connections.c inet_ntop_cache.c network.c - network_darwin_sendfile.c - network_freebsd_sendfile.c - network_linux_sendfile.c - network_solaris_sendfilev.c network_write.c - network_write_mmap.c - network_write_no_mmap.c - network_writev.c configfile.c configparser.c request.c diff --git a/src/Makefile.am b/src/Makefile.am index c16fc449..ca9e1710 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -82,11 +82,7 @@ common_src=base64.c buffer.c log.c \ src = server.c response.c connections.c \ inet_ntop_cache.c \ network.c \ - network_write.c network_linux_sendfile.c \ - network_write_mmap.c network_write_no_mmap.c \ - network_freebsd_sendfile.c network_writev.c \ - network_solaris_sendfilev.c \ - network_darwin_sendfile.c \ + network_write.c \ configfile.c configparser.c proc_open.c lib_LTLIBRARIES = @@ -386,7 +382,7 @@ hdr = server.h base64.h buffer.h network.h log.h keyvalue.h \ fdevent.h gw_backend.h connections.h base.h base_decls.h stat_cache.h \ plugin.h \ etag.h joblist.h array.h vector.h crc32.h \ - network_backends.h configfile.h \ + network_write.h configfile.h \ mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ configparser.h mod_ssi_exprparser.h \ rand.h \ diff --git a/src/SConscript b/src/SConscript index d2bfa7d7..c2ec0f54 100644 --- a/src/SConscript +++ b/src/SConscript @@ -77,12 +77,7 @@ common_src = Split("base64.c buffer.c log.c \ src = Split("server.c response.c connections.c \ inet_ntop_cache.c \ network.c \ - network_writev.c \ - network_write_mmap.c network_write_no_mmap.c \ - network_write.c network_linux_sendfile.c \ - network_freebsd_sendfile.c \ - network_solaris_sendfilev.c \ - network_darwin_sendfile.c \ + network_write.c \ configfile.c configparser.c request.c proc_open.c") lemon = env.Program('lemon', 'lemon.c', LIBS = GatherLibs(env)) diff --git a/src/base.h b/src/base.h index cef4e1cd..51736b27 100644 --- a/src/base.h +++ b/src/base.h @@ -574,7 +574,7 @@ struct server { int event_handler; - int (* network_backend_write)(struct server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); + int (* network_backend_write)(struct server *srv, int fd, chunkqueue *cq, off_t max_bytes); handler_t (* request_env)(struct server *srv, connection *con); uid_t uid; diff --git a/src/connections.c b/src/connections.c index bf271bb4..985667e9 100644 --- a/src/connections.c +++ b/src/connections.c @@ -1058,7 +1058,7 @@ static int connection_read_cq(server *srv, connection *con, chunkqueue *cq, off_ static int connection_write_cq(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) { - return srv->network_backend_write(srv, con, con->fd, cq, max_bytes); + return srv->network_backend_write(srv, con->fd, cq, max_bytes); } diff --git a/src/gw_backend.c b/src/gw_backend.c index c24332b6..91f15c5d 100644 --- a/src/gw_backend.c +++ b/src/gw_backend.c @@ -1816,7 +1816,6 @@ static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) { /* fall through */ case GW_STATE_WRITE: if (!chunkqueue_is_empty(hctx->wb)) { - connection *con = hctx->remote_conn; int ret; #if 0 if (hctx->conf.debug > 1) { @@ -1825,7 +1824,7 @@ static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) { "), size =", chunkqueue_length(hctx->wb)); } #endif - ret = srv->network_backend_write(srv, con, hctx->fd, hctx->wb, + ret = srv->network_backend_write(srv, hctx->fd, hctx->wb, MAX_WRITE_LIMIT); chunkqueue_remove_finished_chunks(hctx->wb); diff --git a/src/meson.build b/src/meson.build index 08a4c8b0..8f696aa9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -571,14 +571,7 @@ main_src = [ 'configfile.c', 'connections.c', 'inet_ntop_cache.c', - 'network_darwin_sendfile.c', - 'network_freebsd_sendfile.c', - 'network_linux_sendfile.c', - 'network_solaris_sendfilev.c', - 'network_write_mmap.c', - 'network_write_no_mmap.c', 'network_write.c', - 'network_writev.c', 'network.c', 'proc_open.c', 'request.c', diff --git a/src/network.c b/src/network.c index 51dce3cc..17dc3f79 100644 --- a/src/network.c +++ b/src/network.c @@ -8,7 +8,7 @@ #include "configfile.h" #include "sock_addr.h" -#include "network_backends.h" +#include "network_write.h" #include "sys-socket.h" #include @@ -385,81 +385,9 @@ int network_close(server *srv) { return 0; } -typedef enum { - NETWORK_BACKEND_UNSET, - NETWORK_BACKEND_WRITE, - NETWORK_BACKEND_WRITEV, - NETWORK_BACKEND_SENDFILE, -} network_backend_t; - int network_init(server *srv, int stdin_fd) { size_t i; - network_backend_t backend; - - struct nb_map { - network_backend_t nb; - const char *name; - } network_backends[] = { - /* lowest id wins */ -#if defined USE_SENDFILE - { NETWORK_BACKEND_SENDFILE, "sendfile" }, -#endif -#if defined USE_LINUX_SENDFILE - { NETWORK_BACKEND_SENDFILE, "linux-sendfile" }, -#endif -#if defined USE_FREEBSD_SENDFILE - { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" }, -#endif -#if defined USE_SOLARIS_SENDFILEV - { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" }, -#endif -#if defined USE_WRITEV - { NETWORK_BACKEND_WRITEV, "writev" }, -#endif - { NETWORK_BACKEND_WRITE, "write" }, - { NETWORK_BACKEND_UNSET, NULL } - }; - - /* get a useful default */ - backend = network_backends[0].nb; - - /* match name against known types */ - if (!buffer_string_is_empty(srv->srvconf.network_backend)) { - for (i = 0; network_backends[i].name; i++) { - /**/ - if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) { - backend = network_backends[i].nb; - break; - } - } - if (NULL == network_backends[i].name) { - /* we don't know it */ - - log_error_write(srv, __FILE__, __LINE__, "sb", - "server.network-backend has a unknown value:", - srv->srvconf.network_backend); - - return -1; - } - } - - switch(backend) { - case NETWORK_BACKEND_WRITE: - srv->network_backend_write = network_write_chunkqueue_write; - break; -#if defined(USE_WRITEV) - case NETWORK_BACKEND_WRITEV: - srv->network_backend_write = network_write_chunkqueue_writev; - break; -#endif -#if defined(USE_SENDFILE) - case NETWORK_BACKEND_SENDFILE: - srv->network_backend_write = network_write_chunkqueue_sendfile; - break; -#endif - default: - return -1; - } + if (0 != network_write_init(srv)) return -1; { int rc; diff --git a/src/network_backends.h b/src/network_backends.h deleted file mode 100644 index db03a589..00000000 --- a/src/network_backends.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef _NETWORK_BACKENDS_H_ -#define _NETWORK_BACKENDS_H_ -#include "first.h" - -#include "settings.h" - -#include - -/* on linux 2.4.x you get either sendfile or LFS */ -#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN -# ifdef USE_SENDFILE -# error "can't have more than one sendfile implementation" -# endif -# define USE_SENDFILE "linux-sendfile" -# define USE_LINUX_SENDFILE -#endif - -#if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__)) -# ifdef USE_SENDFILE -# error "can't have more than one sendfile implementation" -# endif -# define USE_SENDFILE "freebsd-sendfile" -# define USE_FREEBSD_SENDFILE -#endif - -#if defined HAVE_SENDFILE && defined(__APPLE__) -# ifdef USE_SENDFILE -# error "can't have more than one sendfile implementation" -# endif -# define USE_SENDFILE "darwin-sendfile" -# define USE_DARWIN_SENDFILE -#endif - -#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun) -# ifdef USE_SENDFILE -# error "can't have more than one sendfile implementation" -# endif -# define USE_SENDFILE "solaris-sendfilev" -# define USE_SOLARIS_SENDFILEV -#endif - -/* not supported so far -#if defined HAVE_SEND_FILE && defined(__aix) -# ifdef USE_SENDFILE -# error "can't have more than one sendfile implementation" -# endif -# define USE_SENDFILE "aix-sendfile" -# define USE_AIX_SENDFILE -#endif -*/ - -#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV -# define USE_WRITEV -#endif - -#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP -# define USE_MMAP -#endif - -#include "base.h" - -/* return values: - * >= 0 : no error - * -1 : error (on our side) - * -2 : remote close - */ - -int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); - -#if defined(USE_WRITEV) -int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); /* fallback to write */ -#endif - -#if defined(USE_SENDFILE) -int network_write_chunkqueue_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); /* fallback to write */ -#endif - -/* write next chunk(s); finished chunks are removed afterwards after successful writes. - * return values: similar as backends (0 succes, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */ -/* next chunk must be MEM_CHUNK. use write()/send() */ -int network_write_mem_chunk(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes); - -#if defined(USE_WRITEV) -/* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */ -int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes); -#else -/* fallback to write()/send() */ -static inline int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - return network_write_mem_chunk(srv, con, fd, cq, p_max_bytes); -} -#endif - -/* next chunk must be FILE_CHUNK. use temporary buffer (srv->tmp_buf) to read into, then write()/send() it */ -int network_write_file_chunk_no_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes); - -off_t mmap_align_offset(off_t start); -#if defined(USE_MMAP) -/* next chunk must be FILE_CHUNK. send mmap()ed file with write() */ -int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes); -#else -/* fallback to no_mmap */ -static inline int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes); -} -#endif - -#if defined(USE_SENDFILE) -int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes); -#else -/* fallback to mmap */ -static inline int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes); -} -#endif - -/* next chunk must be FILE_CHUNK. return values: 0 success (=> -1 != cq->first->file.fd), -1 error */ -int network_open_file_chunk(server *srv, connection *con, chunkqueue *cq); - -#endif diff --git a/src/network_darwin_sendfile.c b/src/network_darwin_sendfile.c deleted file mode 100644 index 9b4ba6cf..00000000 --- a/src/network_darwin_sendfile.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "first.h" - -#include "network_backends.h" - -#if defined(USE_DARWIN_SENDFILE) - -#include "log.h" - -#include -#include -#include - -#include -#include - -int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - off_t offset, written = 0; - off_t toSend; - int r; - - force_assert(NULL != c); - force_assert(FILE_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= c->file.length); - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - if (toSend > *p_max_bytes) toSend = *p_max_bytes; - - if (0 == toSend) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; - - /* Darwin sendfile() */ - written = toSend; - if (-1 == (r = sendfile(c->file.fd, fd, offset, &written, NULL, 0))) { - switch(errno) { - case EAGAIN: - case EINTR: - /* for EAGAIN/EINTR written still contains the sent bytes */ - break; /* try again later */ - case EPIPE: - case ENOTCONN: - return -2; - case EINVAL: - case ENOSYS: - #if defined(ENOTSUP) \ - && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) - case ENOTSUP: - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - #endif - #ifdef USE_MMAP - return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes); - #else - return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes); - #endif - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); - return -1; - } - } - - if (written >= 0) { - chunkqueue_mark_written(cq, written); - *p_max_bytes -= written; - } - - return (r >= 0 && written == toSend) ? 0 : -3; -} - -#endif /* USE_DARWIN_SENDFILE */ diff --git a/src/network_freebsd_sendfile.c b/src/network_freebsd_sendfile.c deleted file mode 100644 index 11da4e85..00000000 --- a/src/network_freebsd_sendfile.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "first.h" - -#include "network_backends.h" - -#if defined(USE_FREEBSD_SENDFILE) - -#include "log.h" - -#include -#include -#include - -#include -#include - -int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - off_t offset, written = 0; - off_t toSend; - int r; - - force_assert(NULL != c); - force_assert(FILE_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= c->file.length); - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - if (toSend > *p_max_bytes) toSend = *p_max_bytes; - - if (0 == toSend) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; - - /* FreeBSD sendfile() */ - if (-1 == (r = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0))) { - switch(errno) { - case EAGAIN: - case EINTR: - /* for EAGAIN/EINTR written still contains the sent bytes */ - break; /* try again later */ - case EPIPE: - case ENOTCONN: - return -2; - case EINVAL: - case ENOSYS: - #if defined(ENOTSUP) \ - && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) - case ENOTSUP: - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - #endif - #ifdef USE_MMAP - return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes); - #else - return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes); - #endif - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); - return -1; - } - } - - if (written >= 0) { - chunkqueue_mark_written(cq, written); - *p_max_bytes -= written; - } - - return (r >= 0 && written == toSend) ? 0 : -3; -} - -#endif /* USE_FREEBSD_SENDFILE */ diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c deleted file mode 100644 index a15ef252..00000000 --- a/src/network_linux_sendfile.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "first.h" - -#include "network_backends.h" - -#if defined(USE_LINUX_SENDFILE) - -#include "log.h" - -#include - -#include -#include - -int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - ssize_t r; - off_t offset; - off_t toSend; - - force_assert(NULL != c); - force_assert(FILE_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= c->file.length); - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - if (toSend > *p_max_bytes) toSend = *p_max_bytes; - - if (0 == toSend) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; - - if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) { - switch (errno) { - case EAGAIN: - case EINTR: - break; - case EPIPE: - case ECONNRESET: - return -2; - case EINVAL: - case ENOSYS: - #if defined(ENOTSUP) \ - && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) - case ENOTSUP: - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - #endif - #ifdef USE_MMAP - return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes); - #else - return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes); - #endif - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "sendfile failed:", strerror(errno), fd); - return -1; - } - } - - if (r >= 0) { - chunkqueue_mark_written(cq, r); - *p_max_bytes -= r; - } - - return (r > 0 && r == toSend) ? 0 : -3; -} - -#endif /* USE_LINUX_SENDFILE */ diff --git a/src/network_solaris_sendfilev.c b/src/network_solaris_sendfilev.c deleted file mode 100644 index d0e5007a..00000000 --- a/src/network_solaris_sendfilev.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "first.h" - -#include "network_backends.h" - -#if defined(USE_SOLARIS_SENDFILEV) - -#include "log.h" - -#include - -#include -#include - -/** - * a very simple sendfilev() interface for solaris which can be optimised a lot more - * as solaris sendfilev() supports 'sending everythin in one syscall()' - */ - -int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - off_t offset; - off_t toSend; - size_t written = 0; - int r; - sendfilevec_t fvec; - - force_assert(NULL != c); - force_assert(FILE_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= c->file.length); - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - if (toSend > *p_max_bytes) toSend = *p_max_bytes; - - if (0 == toSend) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; - - fvec.sfv_fd = c->file.fd; - fvec.sfv_flag = 0; - fvec.sfv_off = offset; - fvec.sfv_len = toSend; - - /* Solaris sendfilev() */ - - if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) { - switch(errno) { - case EAGAIN: - case EINTR: - /* for EAGAIN/EINTR written still contains the sent bytes */ - break; /* try again later */ - case EPIPE: - case ENOTCONN: - return -2; - case EINVAL: - case ENOSYS: - #if defined(ENOTSUP) \ - && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) - case ENOTSUP: - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - #endif - #ifdef USE_MMAP - return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes); - #else - return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes); - #endif - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); - return -1; - } - } - - if (written >= 0) { - chunkqueue_mark_written(cq, written); - *p_max_bytes -= written; - } - - return (r >= 0 && (off_t) written == toSend) ? 0 : -3; -} - -#endif /* USE_SOLARIS_SENDFILEV */ diff --git a/src/network_write.c b/src/network_write.c dissimilarity index 90% index a346e937..240e9b59 100644 --- a/src/network_write.c +++ b/src/network_write.c @@ -1,115 +1,737 @@ -#include "first.h" - -#include "network_backends.h" - -#include "network.h" -#include "log.h" - -#include "sys-socket.h" - -#include - -#include -#include - -int network_write_mem_chunk(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - off_t c_len; - ssize_t r; - UNUSED(con); - - force_assert(NULL != c); - force_assert(MEM_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem)); - - c_len = buffer_string_length(c->mem) - c->offset; - if (c_len > *p_max_bytes) c_len = *p_max_bytes; - - if (0 == c_len) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - -#if defined(__WIN32) - if ((r = send(fd, c->mem->ptr + c->offset, c_len, 0)) < 0) { - int lastError = WSAGetLastError(); - switch (lastError) { - case WSAEINTR: - case WSAEWOULDBLOCK: - break; - case WSAECONNRESET: - case WSAETIMEDOUT: - case WSAECONNABORTED: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "sdd", - "send failed: ", lastError, fd); - return -1; - } - } -#else /* __WIN32 */ - if ((r = write(fd, c->mem->ptr + c->offset, c_len)) < 0) { - switch (errno) { - case EAGAIN: - case EINTR: - break; - case EPIPE: - case ECONNRESET: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "write failed:", strerror(errno), fd); - return -1; - } - } -#endif /* __WIN32 */ - - if (r >= 0) { - *p_max_bytes -= r; - chunkqueue_mark_written(cq, r); - } - - return (r > 0 && r == c_len) ? 0 : -3; -} - -int network_write_chunkqueue_write(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { - while (max_bytes > 0 && NULL != cq->first) { - int r = -1; - - switch (cq->first->type) { - case MEM_CHUNK: - r = network_write_mem_chunk(srv, con, fd, cq, &max_bytes); - break; - case FILE_CHUNK: - r = network_write_file_chunk_mmap(srv, con, fd, cq, &max_bytes); - break; - } - - if (-3 == r) return 0; - if (0 != r) return r; - } - - return 0; -} - -#if defined(USE_SENDFILE) -int network_write_chunkqueue_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { - while (max_bytes > 0 && NULL != cq->first) { - int r = -1; - - switch (cq->first->type) { - case MEM_CHUNK: - r = network_writev_mem_chunks(srv, con, fd, cq, &max_bytes); - break; - case FILE_CHUNK: - r = network_write_file_chunk_sendfile(srv, con, fd, cq, &max_bytes); - break; - } - - if (-3 == r) return 0; - if (0 != r) return r; - } - - return 0; -} -#endif +#include "first.h" + +#include "network_write.h" + +#include "base.h" +#include "log.h" + +#include +#include "sys-socket.h" + +#include +#include +#include + + +/* on linux 2.4.x you get either sendfile or LFS */ +#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \ + && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \ + && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "linux-sendfile" +# define NETWORK_WRITE_USE_LINUX_SENDFILE +#endif + +#if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__)) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "freebsd-sendfile" +# define NETWORK_WRITE_USE_FREEBSD_SENDFILE +#endif + +#if defined HAVE_SENDFILE && defined(__APPLE__) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "darwin-sendfile" +# define NETWORK_WRITE_USE_DARWIN_SENDFILE +#endif + +#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "solaris-sendfilev" +# define NETWORK_WRITE_USE_SOLARIS_SENDFILEV +#endif + +/* not supported so far +#if defined HAVE_SEND_FILE && defined(__aix) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "aix-sendfile" +# define NETWORK_WRITE_USE_AIX_SENDFILE +#endif +*/ + +#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV +# define NETWORK_WRITE_USE_WRITEV +#endif + +#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP +# define NETWORK_WRITE_USE_MMAP +#endif + + +static int network_write_error(server *srv, int fd) { + #if defined(__WIN32) + int lastError = WSAGetLastError(); + switch (lastError) { + case WSAEINTR: + case WSAEWOULDBLOCK: + return -3; + case WSAECONNRESET: + case WSAETIMEDOUT: + case WSAECONNABORTED: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", + "send failed: ", lastError, fd); + return -1; + } + #else /* __WIN32 */ + switch (errno) { + case EAGAIN: + case EINTR: + return -3; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), fd); + return -1; + } + #endif /* __WIN32 */ +} + +inline +static ssize_t network_write_data_len(int fd, const char *data, off_t len) { + #if defined(__WIN32) + return send(fd, data, len, 0); + #else /* __WIN32 */ + return write(fd, data, len); + #endif /* __WIN32 */ +} + + + + +/* write next chunk(s); finished chunks are removed afterwards after successful writes. + * return values: similar as backends (0 succes, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */ +/* next chunk must be MEM_CHUNK. use write()/send() */ +static int network_write_mem_chunk(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk* const c = cq->first; + ssize_t wr; + off_t c_len = (off_t)buffer_string_length(c->mem); + force_assert(c->offset >= 0 && c->offset <= c_len); + c_len -= c->offset; + if (c_len > *p_max_bytes) c_len = *p_max_bytes; + + if (0 == c_len) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + wr = network_write_data_len(fd, c->mem->ptr + c->offset, c_len); + if (wr >= 0) { + *p_max_bytes -= wr; + chunkqueue_mark_written(cq, wr); + return (wr > 0 && wr == c_len) ? 0 : -3; + } else { + return network_write_error(srv, fd); + } +} + + + + +#if !defined(NETWORK_WRITE_USE_MMAP) + +static int network_write_file_chunk_no_mmap(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk* const c = cq->first; + off_t offset, toSend; + ssize_t wr; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + if (toSend > *p_max_bytes) toSend = *p_max_bytes; + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + if (toSend > 64*1024) toSend = 64*1024; /* max read 64kb in one step */ + buffer_string_prepare_copy(srv->tmp_buf, toSend); + + if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { + log_error_write(srv, __FILE__, __LINE__, "ss","lseek:",strerror(errno)); + return -1; + } + if (-1 == (toSend = read(c->file.fd, srv->tmp_buf->ptr, toSend))) { + log_error_write(srv, __FILE__, __LINE__, "ss","read:",strerror(errno)); + return -1; + } + + wr = network_write_data_len(fd, srv->tmp_buf->ptr, toSend); + if (wr >= 0) { + *p_max_bytes -= wr; + chunkqueue_mark_written(cq, wr); + return (wr > 0 && wr == toSend) ? 0 : -3; + } else { + return network_write_error(srv, fd); + } +} + +#endif + + + + +#if defined(NETWORK_WRITE_USE_MMAP) + +#include "sys-mmap.h" + +#include +#include + +#define MMAP_CHUNK_SIZE (512*1024) + +static off_t mmap_align_offset(off_t start) { + static long pagesize = 0; + if (0 == pagesize) { + pagesize = sysconf(_SC_PAGESIZE); + force_assert(pagesize < MMAP_CHUNK_SIZE); + } + force_assert(start >= (start % pagesize)); + return start - (start % pagesize); +} + +static volatile int sigbus_jmp_valid; +static sigjmp_buf sigbus_jmp; + +static void sigbus_handler(int sig) { + UNUSED(sig); + if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); + log_failed_assert(__FILE__, __LINE__, "SIGBUS"); +} + +/* next chunk must be FILE_CHUNK. send mmap()ed file with write() */ +static int network_write_file_chunk_mmap(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk* const c = cq->first; + off_t offset, toSend, file_end; + ssize_t r; + size_t mmap_offset, mmap_avail; + const char *data; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + if (toSend > *p_max_bytes) toSend = *p_max_bytes; + file_end = c->file.start + c->file.length; /*file end offset in this chunk*/ + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + /* mmap buffer if offset is outside old mmap area or not mapped at all */ + if (MAP_FAILED == c->file.mmap.start + || offset < c->file.mmap.offset + || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) { + + if (MAP_FAILED != c->file.mmap.start) { + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.start = MAP_FAILED; + } + + /* Optimizations for the future: + * + * adaptive mem-mapping + * the problem: + * we mmap() the whole file. If someone has alot large files and + * 32-bit machine the virtual address area will be unrun and we + * will have a failing mmap() call. + * solution: + * only mmap 16M in one chunk and move the window as soon as we have + * finished the first 8M + * + * read-ahead buffering + * the problem: + * sending out several large files in parallel trashes read-ahead + * of the kernel leading to long wait-for-seek times. + * solutions: (increasing complexity) + * 1. use madvise + * 2. use a internal read-ahead buffer in the chunk-structure + * 3. use non-blocking IO for file-transfers + * */ + + c->file.mmap.offset = mmap_align_offset(offset); + + /* all mmap()ed areas are MMAP_CHUNK_SIZE + * except the last which might be smaller */ + c->file.mmap.length = MMAP_CHUNK_SIZE; + if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) { + c->file.mmap.length = file_end - c->file.mmap.offset; + } + + c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, + MAP_SHARED, c->file.fd, c->file.mmap.offset); + if (MAP_FAILED == c->file.mmap.start) { + log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:", + strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length); + return -1; + } + + #if defined(HAVE_MADVISE) + /* don't advise files < 64Kb */ + if (c->file.mmap.length > (64*1024)) { + /* darwin 7 is returning EINVAL all the time and I don't know how to + * detect this at runtime. + * + * ignore the return value for now */ + madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); + } + #endif + } + + force_assert(offset >= c->file.mmap.offset); + mmap_offset = offset - c->file.mmap.offset; + force_assert(c->file.mmap.length > mmap_offset); + mmap_avail = c->file.mmap.length - mmap_offset; + if (toSend > (off_t) mmap_avail) toSend = mmap_avail; + + data = c->file.mmap.start + mmap_offset; + + /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */ + if (0 == sigsetjmp(sigbus_jmp, 1)) { + signal(SIGBUS, sigbus_handler); + + sigbus_jmp_valid = 1; + r = network_write_data_len(fd, data, toSend); + sigbus_jmp_valid = 0; + } else { + sigbus_jmp_valid = 0; + + log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", + c->file.name, c->file.fd); + + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.start = MAP_FAILED; + return -1; + } + + if (r >= 0) { + *p_max_bytes -= r; + chunkqueue_mark_written(cq, r); + return (r > 0 && r == toSend) ? 0 : -3; + } else { + return network_write_error(srv, fd); + } +} + +#endif /* NETWORK_WRITE_USE_MMAP */ + + + + +#if defined(NETWORK_WRITE_USE_WRITEV) + +#if defined(HAVE_SYS_UIO_H) +# include +#endif + +#if defined(UIO_MAXIOV) +# define SYS_MAX_CHUNKS UIO_MAXIOV +#elif defined(IOV_MAX) +/* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */ +# define SYS_MAX_CHUNKS IOV_MAX +#elif defined(_XOPEN_IOV_MAX) +/* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */ +# define SYS_MAX_CHUNKS _XOPEN_IOV_MAX +#else +# error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined +#endif + +/* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit: + * - each entry will use 1 pointer + 1 size_t + * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers) + */ +#define STACK_MAX_ALLOC_CHUNKS 32 +#if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS +# define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS +#else +# define MAX_CHUNKS SYS_MAX_CHUNKS +#endif + +/* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */ +static int network_writev_mem_chunks(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + struct iovec chunks[MAX_CHUNKS]; + size_t num_chunks = 0; + off_t max_bytes = *p_max_bytes; + off_t toSend = 0; + ssize_t r; + + for (const chunk *c = cq->first; + NULL != c && MEM_CHUNK == c->type + && num_chunks < MAX_CHUNKS && toSend < max_bytes; + c = c->next) { + size_t c_len = buffer_string_length(c->mem); + force_assert(c->offset >= 0 && c->offset <= (off_t)c_len); + c_len -= c->offset; + if (c_len > 0) { + toSend += c_len; + + chunks[num_chunks].iov_base = c->mem->ptr + c->offset; + chunks[num_chunks].iov_len = c_len; + + ++num_chunks; + } + } + + if (0 == num_chunks) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + r = writev(fd, chunks, num_chunks); + + if (r < 0) switch (errno) { + case EAGAIN: + case EINTR: + break; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "writev failed:", strerror(errno), fd); + return -1; + } + + if (r >= 0) { + *p_max_bytes -= r; + chunkqueue_mark_written(cq, r); + } + + return (r > 0 && r == toSend) ? 0 : -3; +} + +#endif /* NETWORK_WRITE_USE_WRITEV */ + + + + +#if defined(NETWORK_WRITE_USE_SENDFILE) + +#if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) \ + || defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV) +#include +#endif + +#if defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) \ + || defined(NETWORK_WRITE_USE_DARWIN_SENDFILE) +#include +#endif + +static int network_write_file_chunk_sendfile(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk * const c = cq->first; + ssize_t r; + off_t offset; + off_t toSend; + off_t written = 0; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + if (toSend > *p_max_bytes) toSend = *p_max_bytes; + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + /* Darwin, FreeBSD, and Solaris variants support iovecs and could + * be optimized to send more than just file in single syscall */ + + #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) + + r = sendfile(fd, c->file.fd, &offset, toSend); + if (r > 0) written = (off_t)r; + + #elif defined(NETWORK_WRITE_USE_DARWIN_SENDFILE) + + written = toSend; + r = sendfile(c->file.fd, fd, offset, &written, NULL, 0); + /* (for EAGAIN/EINTR written still contains the sent bytes) */ + + #elif defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) + + r = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0); + /* (for EAGAIN/EINTR written still contains the sent bytes) */ + + #elif defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV) + { + sendfilevec_t fvec; + fvec.sfv_fd = c->file.fd; + fvec.sfv_flag = 0; + fvec.sfv_off = offset; + fvec.sfv_len = toSend; + + /* Solaris sendfilev() */ + r = sendfilev(fd, &fvec, 1, (size_t *)&written); + /* (for EAGAIN/EINTR written still contains the sent bytes) */ + } + #else + + r = -1; + errno = ENOSYS; + + #endif + + if (-1 == r) { + switch(errno) { + case EAGAIN: + case EINTR: + break; /* try again later */ + case EPIPE: + case ECONNRESET: + case ENOTCONN: + return -2; + case EINVAL: + case ENOSYS: + #if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) + case ENOTSUP: + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + #endif + #ifdef NETWORK_WRITE_USE_MMAP + return network_write_file_chunk_mmap(srv, fd, cq, p_max_bytes); + #else + return network_write_file_chunk_no_mmap(srv, fd, cq, p_max_bytes); + #endif + default: + log_error_write(srv, __FILE__, __LINE__, "ssdSd", + "sendfile():", strerror(errno), errno, "fd:", fd); + return -1; + } + } + + if (written >= 0) { /*(always true)*/ + chunkqueue_mark_written(cq, written); + *p_max_bytes -= written; + } + + return (r >= 0 && written == toSend) ? 0 : -3; +} + +#endif + + + + +/* return values: + * >= 0 : no error + * -1 : error (on our side) + * -2 : remote close + */ + +static int network_write_chunkqueue_write(server *srv, int fd, chunkqueue *cq, off_t max_bytes) { + while (max_bytes > 0 && NULL != cq->first) { + int r = -1; + + switch (cq->first->type) { + case MEM_CHUNK: + r = network_write_mem_chunk(srv, fd, cq, &max_bytes); + break; + case FILE_CHUNK: + #ifdef NETWORK_WRITE_USE_MMAP + r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes); + #else + r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes); + #endif + break; + } + + if (-3 == r) return 0; + if (0 != r) return r; + } + + return 0; +} + +#if defined(NETWORK_WRITE_USE_WRITEV) +static int network_write_chunkqueue_writev(server *srv, int fd, chunkqueue *cq, off_t max_bytes) { + while (max_bytes > 0 && NULL != cq->first) { + int r = -1; + + switch (cq->first->type) { + case MEM_CHUNK: + #if defined(NETWORK_WRITE_USE_WRITEV) + r = network_writev_mem_chunks(srv, fd, cq, &max_bytes); + #else + r = network_write_mem_chunk(srv, fd, cq, &max_bytes); + #endif + break; + case FILE_CHUNK: + #ifdef NETWORK_WRITE_USE_MMAP + r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes); + #else + r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes); + #endif + break; + } + + if (-3 == r) return 0; + if (0 != r) return r; + } + + return 0; +} +#endif + +#if defined(NETWORK_WRITE_USE_SENDFILE) +static int network_write_chunkqueue_sendfile(server *srv, int fd, chunkqueue *cq, off_t max_bytes) { + while (max_bytes > 0 && NULL != cq->first) { + int r = -1; + + switch (cq->first->type) { + case MEM_CHUNK: + #if defined(NETWORK_WRITE_USE_WRITEV) + r = network_writev_mem_chunks(srv, fd, cq, &max_bytes); + #else + r = network_write_mem_chunk(srv, fd, cq, &max_bytes); + #endif + break; + case FILE_CHUNK: + #if defined(NETWORK_WRITE_USE_SENDFILE) + r = network_write_file_chunk_sendfile(srv, fd, cq, &max_bytes); + #elif defined(NETWORK_WRITE_USE_MMAP) + r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes); + #else + r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes); + #endif + break; + } + + if (-3 == r) return 0; + if (0 != r) return r; + } + + return 0; +} +#endif + +int network_write_init(server *srv) { + typedef enum { + NETWORK_BACKEND_UNSET, + NETWORK_BACKEND_WRITE, + NETWORK_BACKEND_WRITEV, + NETWORK_BACKEND_SENDFILE, + } network_backend_t; + + network_backend_t backend; + + struct nb_map { + network_backend_t nb; + const char *name; + } network_backends[] = { + /* lowest id wins */ + { NETWORK_BACKEND_SENDFILE, "sendfile" }, + { NETWORK_BACKEND_SENDFILE, "linux-sendfile" }, + { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" }, + { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" }, + { NETWORK_BACKEND_WRITEV, "writev" }, + { NETWORK_BACKEND_WRITE, "write" }, + { NETWORK_BACKEND_UNSET, NULL } + }; + + /* get a useful default */ + backend = network_backends[0].nb; + + /* match name against known types */ + if (!buffer_string_is_empty(srv->srvconf.network_backend)) { + const char *name; + for (size_t i = 0; NULL != (name = network_backends[i].name); ++i) { + if (0 == strcmp(srv->srvconf.network_backend->ptr, name)) { + backend = network_backends[i].nb; + break; + } + } + if (NULL == name) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.network-backend has an unknown value:", + srv->srvconf.network_backend); + return -1; + } + } + + switch(backend) { + case NETWORK_BACKEND_SENDFILE: + #if defined(NETWORK_WRITE_USE_SENDFILE) + srv->network_backend_write = network_write_chunkqueue_sendfile; + break; + #endif + case NETWORK_BACKEND_WRITEV: + #if defined(NETWORK_WRITE_USE_WRITEV) + srv->network_backend_write = network_write_chunkqueue_writev; + break; + #endif + case NETWORK_BACKEND_WRITE: + srv->network_backend_write = network_write_chunkqueue_write; + break; + default: + return -1; + } + + return 0; +} + +const char * network_write_show_handlers(void) { + return + "\nNetwork handler:\n\n" + #if defined NETWORK_WRITE_USE_LINUX_SENDFILE + "\t+ linux-sendfile\n" + #else + "\t- linux-sendfile\n" + #endif + #if defined NETWORK_WRITE_USE_FREEBSD_SENDFILE + "\t+ freebsd-sendfile\n" + #else + "\t- freebsd-sendfile\n" + #endif + #if defined NETWORK_WRITE_USE_DARWIN_SENDFILE + "\t+ darwin-sendfile\n" + #else + "\t- darwin-sendfile\n" + #endif + #if defined NETWORK_WRITE_USE_SOLARIS_SENDFILEV + "\t+ solaris-sendfilev\n" + #else + "\t- solaris-sendfilev\n" + #endif + #if defined NETWORK_WRITE_USE_WRITEV + "\t+ writev\n" + #else + "\t- writev\n" + #endif + "\t+ write\n" + #ifdef NETWORK_WRITE_USE_MMAP + "\t+ mmap support\n" + #else + "\t- mmap support\n" + #endif + ; +} diff --git a/src/network_write.h b/src/network_write.h new file mode 100644 index 00000000..a80cbcf8 --- /dev/null +++ b/src/network_write.h @@ -0,0 +1,9 @@ +#ifndef INCLUDED_NETWORK_WRITE_H +#define INCLUDED_NETWORK_WRITE_H +#include "first.h" +#include "base_decls.h" + +int network_write_init(server *srv); +const char * network_write_show_handlers(void); + +#endif diff --git a/src/network_write_mmap.c b/src/network_write_mmap.c deleted file mode 100644 index d46c1adf..00000000 --- a/src/network_write_mmap.c +++ /dev/null @@ -1,210 +0,0 @@ -#include "first.h" - -#include "network_backends.h" - -#include "log.h" -#include "sys-mmap.h" - -#include -#include -#include - -#include -#include - -#define MMAP_CHUNK_SIZE (512*1024) - -off_t mmap_align_offset(off_t start) { - static long pagesize = 0; - if (0 == pagesize) { - pagesize = sysconf(_SC_PAGESIZE); - force_assert(pagesize < MMAP_CHUNK_SIZE); - } - force_assert(start >= (start % pagesize)); - return start - (start % pagesize); -} - -#if defined(USE_MMAP) - -static volatile int sigbus_jmp_valid; -static sigjmp_buf sigbus_jmp; - -static void sigbus_handler(int sig) { - UNUSED(sig); - if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); - log_failed_assert(__FILE__, __LINE__, "SIGBUS"); -} - -#if 0 -/* read mmap()ed data into local buffer */ -#define LOCAL_BUFFERING 1 -#endif - -int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - off_t offset, toSend, file_end; - ssize_t r; - size_t mmap_offset, mmap_avail; - const char *data; - UNUSED(con); - - force_assert(NULL != c); - force_assert(FILE_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= c->file.length); - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - if (toSend > *p_max_bytes) toSend = *p_max_bytes; - file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ - - if (0 == toSend) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; - - /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */ - if (0 != sigsetjmp(sigbus_jmp, 1)) { - sigbus_jmp_valid = 0; - - log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", - c->file.name, c->file.fd); - - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; -#ifdef LOCAL_BUFFERING - buffer_reset(c->mem); -#endif - - return -1; - } - - signal(SIGBUS, sigbus_handler); - - /* mmap the buffer if offset is outside old mmap area or not mapped at all */ - if (MAP_FAILED == c->file.mmap.start - || offset < c->file.mmap.offset - || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) { - - if (MAP_FAILED != c->file.mmap.start) { - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - } - - /* Optimizations for the future: - * - * adaptive mem-mapping - * the problem: - * we mmap() the whole file. If someone has alot large files and 32bit - * machine the virtual address area will be unrun and we will have a failing - * mmap() call. - * solution: - * only mmap 16M in one chunk and move the window as soon as we have finished - * the first 8M - * - * read-ahead buffering - * the problem: - * sending out several large files in parallel trashes the read-ahead of the - * kernel leading to long wait-for-seek times. - * solutions: (increasing complexity) - * 1. use madvise - * 2. use a internal read-ahead buffer in the chunk-structure - * 3. use non-blocking IO for file-transfers - * */ - - c->file.mmap.offset = mmap_align_offset(offset); - - /* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */ - c->file.mmap.length = MMAP_CHUNK_SIZE; - if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) { - c->file.mmap.length = file_end - c->file.mmap.offset; - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { - log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:", - strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length); - return -1; - } - -#if defined(LOCAL_BUFFERING) - sigbus_jmp_valid = 1; - buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); - sigbus_jmp_valid = 0; -#else -# if defined(HAVE_MADVISE) - /* don't advise files < 64Kb */ - if (c->file.mmap.length > (64*1024)) { - /* darwin 7 is returning EINVAL all the time and I don't know how to - * detect this at runtime. - * - * ignore the return value for now */ - madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); - } -# endif -#endif - } - - force_assert(offset >= c->file.mmap.offset); - mmap_offset = offset - c->file.mmap.offset; - force_assert(c->file.mmap.length > mmap_offset); - mmap_avail = c->file.mmap.length - mmap_offset; - if (toSend > (off_t) mmap_avail) toSend = mmap_avail; - -#if defined(LOCAL_BUFFERING) - data = c->mem->ptr + mmap_offset; -#else - data = c->file.mmap.start + mmap_offset; -#endif - - sigbus_jmp_valid = 1; -#if defined(__WIN32) - r = send(fd, data, toSend, 0); -#else /* __WIN32 */ - r = write(fd, data, toSend); -#endif /* __WIN32 */ - sigbus_jmp_valid = 0; - -#if defined(__WIN32) - if (r < 0) { - int lastError = WSAGetLastError(); - switch (lastError) { - case WSAEINTR: - case WSAEWOULDBLOCK: - break; - case WSAECONNRESET: - case WSAETIMEDOUT: - case WSAECONNABORTED: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "sdd", - "send failed: ", lastError, fd); - return -1; - } - } -#else /* __WIN32 */ - if (r < 0) { - switch (errno) { - case EAGAIN: - case EINTR: - break; - case EPIPE: - case ECONNRESET: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "write failed:", strerror(errno), fd); - return -1; - } - } -#endif /* __WIN32 */ - - if (r >= 0) { - *p_max_bytes -= r; - chunkqueue_mark_written(cq, r); - } - - return (r > 0 && r == toSend) ? 0 : -3; -} - -#endif /* USE_MMAP */ diff --git a/src/network_write_no_mmap.c b/src/network_write_no_mmap.c deleted file mode 100644 index 2268cc93..00000000 --- a/src/network_write_no_mmap.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "first.h" - -#include "network_backends.h" -#include "log.h" -#include "sys-socket.h" - -#include -#include -#include - -int network_write_file_chunk_no_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - chunk* const c = cq->first; - off_t offset, toSend; - ssize_t r; - UNUSED(con); - - force_assert(NULL != c); - force_assert(FILE_CHUNK == c->type); - force_assert(c->offset >= 0 && c->offset <= c->file.length); - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - if (toSend > 64*1024) toSend = 64*1024; /* max read 64kb in one step */ - if (toSend > *p_max_bytes) toSend = *p_max_bytes; - - if (0 == toSend) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; - - buffer_string_prepare_copy(srv->tmp_buf, toSend); - - if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno)); - return -1; - } - if (-1 == (toSend = read(c->file.fd, srv->tmp_buf->ptr, toSend))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno)); - return -1; - } - -#if defined(__WIN32) - if ((r = send(fd, srv->tmp_buf->ptr, toSend, 0)) < 0) { - int lastError = WSAGetLastError(); - switch (lastError) { - case WSAEINTR: - case WSAEWOULDBLOCK: - break; - case WSAECONNRESET: - case WSAETIMEDOUT: - case WSAECONNABORTED: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "sdd", - "send failed: ", lastError, fd); - return -1; - } - } -#else /* __WIN32 */ - if ((r = write(fd, srv->tmp_buf->ptr, toSend)) < 0) { - switch (errno) { - case EAGAIN: - case EINTR: - break; - case EPIPE: - case ECONNRESET: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "write failed:", strerror(errno), fd); - return -1; - } - } -#endif /* __WIN32 */ - - if (r >= 0) { - *p_max_bytes -= r; - chunkqueue_mark_written(cq, r); - } - - return (r > 0 && r == toSend) ? 0 : -3; -} diff --git a/src/network_writev.c b/src/network_writev.c deleted file mode 100644 index 28edf589..00000000 --- a/src/network_writev.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "first.h" - -#include "network_backends.h" - -#if defined(USE_WRITEV) - -#include "network.h" -#include "log.h" - -#if defined(HAVE_SYS_UIO_H) -# include -#endif - -#include -#include -#include - -#if defined(UIO_MAXIOV) -# define SYS_MAX_CHUNKS UIO_MAXIOV -#elif defined(IOV_MAX) -/* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */ -# define SYS_MAX_CHUNKS IOV_MAX -#elif defined(_XOPEN_IOV_MAX) -/* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */ -# define SYS_MAX_CHUNKS _XOPEN_IOV_MAX -#else -# error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined -#endif - -/* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit: - * - each entry will use 1 pointer + 1 size_t - * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers) - */ -#define STACK_MAX_ALLOC_CHUNKS 32 -#if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS -# define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS -#else -# define MAX_CHUNKS SYS_MAX_CHUNKS -#endif - -int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { - struct iovec chunks[MAX_CHUNKS]; - size_t num_chunks; - off_t max_bytes = *p_max_bytes; - off_t toSend; - ssize_t r; - UNUSED(con); - - force_assert(NULL != cq->first); - force_assert(MEM_CHUNK == cq->first->type); - - { - chunk const *c; - - toSend = 0; - num_chunks = 0; - for (c = cq->first; NULL != c && MEM_CHUNK == c->type && num_chunks < MAX_CHUNKS && toSend < max_bytes; c = c->next) { - size_t c_len; - - force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem)); - c_len = buffer_string_length(c->mem) - c->offset; - if (c_len > 0) { - toSend += c_len; - - chunks[num_chunks].iov_base = c->mem->ptr + c->offset; - chunks[num_chunks].iov_len = c_len; - - ++num_chunks; - } - } - } - - if (0 == num_chunks) { - chunkqueue_remove_finished_chunks(cq); - return 0; - } - - r = writev(fd, chunks, num_chunks); - - if (r < 0) switch (errno) { - case EAGAIN: - case EINTR: - break; - case EPIPE: - case ECONNRESET: - return -2; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "writev failed:", strerror(errno), fd); - return -1; - } - - if (r >= 0) { - *p_max_bytes -= r; - chunkqueue_mark_written(cq, r); - } - - return (r > 0 && r == toSend) ? 0 : -3; -} - -#endif /* USE_WRITEV */ - -int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { - while (max_bytes > 0 && NULL != cq->first) { - int r = -1; - - switch (cq->first->type) { - case MEM_CHUNK: - r = network_writev_mem_chunks(srv, con, fd, cq, &max_bytes); - break; - case FILE_CHUNK: - r = network_write_file_chunk_mmap(srv, con, fd, cq, &max_bytes); - break; - } - - if (-3 == r) return 0; - if (0 != r) return r; - } - - return 0; -} diff --git a/src/server.c b/src/server.c index 4cd8699c..5bbee542 100644 --- a/src/server.c +++ b/src/server.c @@ -18,7 +18,7 @@ #include "stat_cache.h" #include "plugin.h" #include "joblist.h" -#include "network_backends.h" +#include "network_write.h" #ifdef HAVE_VERSIONSTAMP_H # include "versionstamp.h" @@ -524,7 +524,8 @@ static void show_version (void) { } static void show_features (void) { - const char features[] = "" + static const char event_handlers[] = "" + "\nEvent Handlers:\n\n" #ifdef USE_SELECT "\t+ select (generic)\n" #else @@ -560,38 +561,9 @@ static void show_features (void) { #else "\t- libev (generic)\n" #endif - "\nNetwork handler:\n\n" -#if defined USE_LINUX_SENDFILE - "\t+ linux-sendfile\n" -#else - "\t- linux-sendfile\n" -#endif -#if defined USE_FREEBSD_SENDFILE - "\t+ freebsd-sendfile\n" -#else - "\t- freebsd-sendfile\n" -#endif -#if defined USE_DARWIN_SENDFILE - "\t+ darwin-sendfile\n" -#else - "\t- darwin-sendfile\n" -#endif -#if defined USE_SOLARIS_SENDFILEV - "\t+ solaris-sendfilev\n" -#else - "\t- solaris-sendfilev\n" -#endif -#if defined USE_WRITEV - "\t+ writev\n" -#else - "\t- writev\n" -#endif - "\t+ write\n" -#ifdef USE_MMAP - "\t+ mmap support\n" -#else - "\t- mmap support\n" -#endif + ; + + static const char features[] = "\nFeatures:\n\n" #ifdef HAVE_IPV6 "\t+ IPv6 support\n" @@ -678,9 +650,9 @@ static void show_features (void) { #else "\t- GDBM support\n" #endif - "\n"; + ; show_version(); - printf("\nEvent Handlers:\n\n%s", features); + printf("%s%s%s\n", event_handlers, network_write_show_handlers(), features); } static void show_help (void) { -- 2.11.4.GIT