Annotation of embedaddon/lighttpd/src/network_linux_sendfile.c, revision 1.1.1.1

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

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