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>