Annotation of embedaddon/lighttpd/src/network_write_mmap.c, revision 1.1
1.1 ! misho 1: #include "first.h"
! 2:
! 3: #include "network_backends.h"
! 4:
! 5: #include "network.h"
! 6: #include "log.h"
! 7: #include "sys-mmap.h"
! 8:
! 9: #include <setjmp.h>
! 10: #include <signal.h>
! 11: #include <unistd.h>
! 12:
! 13: #include <errno.h>
! 14: #include <string.h>
! 15:
! 16: #define MMAP_CHUNK_SIZE (512*1024)
! 17:
! 18: off_t mmap_align_offset(off_t start) {
! 19: static long pagesize = 0;
! 20: if (0 == pagesize) {
! 21: pagesize = sysconf(_SC_PAGESIZE);
! 22: force_assert(pagesize < MMAP_CHUNK_SIZE);
! 23: }
! 24: force_assert(start >= (start % pagesize));
! 25: return start - (start % pagesize);
! 26: }
! 27:
! 28: #if defined(USE_MMAP)
! 29:
! 30: static volatile int sigbus_jmp_valid;
! 31: static sigjmp_buf sigbus_jmp;
! 32:
! 33: static void sigbus_handler(int sig) {
! 34: UNUSED(sig);
! 35: if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
! 36: log_failed_assert(__FILE__, __LINE__, "SIGBUS");
! 37: }
! 38:
! 39: #if 0
! 40: /* read mmap()ed data into local buffer */
! 41: #define LOCAL_BUFFERING 1
! 42: #endif
! 43:
! 44: int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
! 45: chunk* const c = cq->first;
! 46: off_t offset, toSend, file_end;
! 47: ssize_t r;
! 48: size_t mmap_offset, mmap_avail;
! 49: const char *data;
! 50:
! 51: force_assert(NULL != c);
! 52: force_assert(FILE_CHUNK == c->type);
! 53: force_assert(c->offset >= 0 && c->offset <= c->file.length);
! 54:
! 55: offset = c->file.start + c->offset;
! 56: toSend = c->file.length - c->offset;
! 57: if (toSend > *p_max_bytes) toSend = *p_max_bytes;
! 58: file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
! 59:
! 60: if (0 == toSend) {
! 61: chunkqueue_remove_finished_chunks(cq);
! 62: return 0;
! 63: }
! 64:
! 65: if (0 != network_open_file_chunk(srv, con, cq)) return -1;
! 66:
! 67: /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
! 68: if (0 != sigsetjmp(sigbus_jmp, 1)) {
! 69: sigbus_jmp_valid = 0;
! 70:
! 71: log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:",
! 72: c->file.name, c->file.fd);
! 73:
! 74: munmap(c->file.mmap.start, c->file.mmap.length);
! 75: c->file.mmap.start = MAP_FAILED;
! 76: #ifdef LOCAL_BUFFERING
! 77: buffer_reset(c->mem);
! 78: #endif
! 79:
! 80: return -1;
! 81: }
! 82:
! 83: signal(SIGBUS, sigbus_handler);
! 84:
! 85: /* mmap the buffer if offset is outside old mmap area or not mapped at all */
! 86: if (MAP_FAILED == c->file.mmap.start
! 87: || offset < c->file.mmap.offset
! 88: || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
! 89:
! 90: if (MAP_FAILED != c->file.mmap.start) {
! 91: munmap(c->file.mmap.start, c->file.mmap.length);
! 92: c->file.mmap.start = MAP_FAILED;
! 93: }
! 94:
! 95: /* Optimizations for the future:
! 96: *
! 97: * adaptive mem-mapping
! 98: * the problem:
! 99: * we mmap() the whole file. If someone has alot large files and 32bit
! 100: * machine the virtual address area will be unrun and we will have a failing
! 101: * mmap() call.
! 102: * solution:
! 103: * only mmap 16M in one chunk and move the window as soon as we have finished
! 104: * the first 8M
! 105: *
! 106: * read-ahead buffering
! 107: * the problem:
! 108: * sending out several large files in parallel trashes the read-ahead of the
! 109: * kernel leading to long wait-for-seek times.
! 110: * solutions: (increasing complexity)
! 111: * 1. use madvise
! 112: * 2. use a internal read-ahead buffer in the chunk-structure
! 113: * 3. use non-blocking IO for file-transfers
! 114: * */
! 115:
! 116: c->file.mmap.offset = mmap_align_offset(offset);
! 117:
! 118: /* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */
! 119: c->file.mmap.length = MMAP_CHUNK_SIZE;
! 120: if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) {
! 121: c->file.mmap.length = file_end - c->file.mmap.offset;
! 122: }
! 123:
! 124: if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) {
! 125: log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
! 126: strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
! 127: return -1;
! 128: }
! 129:
! 130: #if defined(LOCAL_BUFFERING)
! 131: sigbus_jmp_valid = 1;
! 132: buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length);
! 133: sigbus_jmp_valid = 0;
! 134: #else
! 135: # if defined(HAVE_MADVISE)
! 136: /* don't advise files < 64Kb */
! 137: if (c->file.mmap.length > (64*1024)) {
! 138: /* darwin 7 is returning EINVAL all the time and I don't know how to
! 139: * detect this at runtime.
! 140: *
! 141: * ignore the return value for now */
! 142: madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED);
! 143: }
! 144: # endif
! 145: #endif
! 146: }
! 147:
! 148: force_assert(offset >= c->file.mmap.offset);
! 149: mmap_offset = offset - c->file.mmap.offset;
! 150: force_assert(c->file.mmap.length > mmap_offset);
! 151: mmap_avail = c->file.mmap.length - mmap_offset;
! 152: if (toSend > (off_t) mmap_avail) toSend = mmap_avail;
! 153:
! 154: #if defined(LOCAL_BUFFERING)
! 155: data = c->mem->ptr + mmap_offset;
! 156: #else
! 157: data = c->file.mmap.start + mmap_offset;
! 158: #endif
! 159:
! 160: sigbus_jmp_valid = 1;
! 161: #if defined(__WIN32)
! 162: r = send(fd, data, toSend, 0);
! 163: #else /* __WIN32 */
! 164: r = write(fd, data, toSend);
! 165: #endif /* __WIN32 */
! 166: sigbus_jmp_valid = 0;
! 167:
! 168: #if defined(__WIN32)
! 169: if (r < 0) {
! 170: int lastError = WSAGetLastError();
! 171: switch (lastError) {
! 172: case WSAEINTR:
! 173: case WSAEWOULDBLOCK:
! 174: break;
! 175: case WSAECONNRESET:
! 176: case WSAETIMEDOUT:
! 177: case WSAECONNABORTED:
! 178: return -2;
! 179: default:
! 180: log_error_write(srv, __FILE__, __LINE__, "sdd",
! 181: "send failed: ", lastError, fd);
! 182: return -1;
! 183: }
! 184: }
! 185: #else /* __WIN32 */
! 186: if (r < 0) {
! 187: switch (errno) {
! 188: case EAGAIN:
! 189: case EINTR:
! 190: break;
! 191: case EPIPE:
! 192: case ECONNRESET:
! 193: return -2;
! 194: default:
! 195: log_error_write(srv, __FILE__, __LINE__, "ssd",
! 196: "write failed:", strerror(errno), fd);
! 197: return -1;
! 198: }
! 199: }
! 200: #endif /* __WIN32 */
! 201:
! 202: if (r >= 0) {
! 203: *p_max_bytes -= r;
! 204: chunkqueue_mark_written(cq, r);
! 205: }
! 206:
! 207: return (r > 0 && r == toSend) ? 0 : -3;
! 208: }
! 209:
! 210: #endif /* USE_MMAP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>