Annotation of embedaddon/lighttpd/src/network_write_mmap.c, revision 1.1.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>