version 1.1.1.1, 2013/10/14 10:32:48
|
version 1.1.1.2, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "network_backends.h" |
#include "network_backends.h" |
|
|
#ifdef USE_SOLARIS_SENDFILEV | #if defined(USE_SOLARIS_SENDFILEV) |
|
|
#include "network.h" |
#include "network.h" |
#include "fdevent.h" |
|
#include "log.h" |
#include "log.h" |
#include "stat_cache.h" |
|
|
|
#include <sys/types.h> | #include <sys/sendfile.h> |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <sys/time.h> | |
#include <sys/resource.h> | |
|
|
#include <netinet/in.h> |
|
#include <netinet/tcp.h> |
|
|
|
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <netdb.h> |
|
#include <string.h> |
#include <string.h> |
#include <stdlib.h> |
|
#include <limits.h> |
|
|
|
#ifndef UIO_MAXIOV |
|
# define UIO_MAXIOV IOV_MAX |
|
#endif |
|
|
|
/** |
/** |
* a very simple sendfilev() interface for solaris which can be optimised a lot more |
* a very simple sendfilev() interface for solaris which can be optimised a lot more |
* as solaris sendfilev() supports 'sending everythin in one syscall()' |
* as solaris sendfilev() supports 'sending everythin in one syscall()' |
* |
|
* If you want such an interface and need the performance, just give me an account on |
|
* a solaris box. |
|
* - jan@kneschke.de |
|
*/ |
*/ |
|
|
|
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; |
|
|
int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { | force_assert(NULL != c); |
chunk *c; | force_assert(FILE_CHUNK == c->type); |
| force_assert(c->offset >= 0 && c->offset <= c->file.length); |
|
|
for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { | offset = c->file.start + c->offset; |
int chunk_finished = 0; | toSend = c->file.length - c->offset; |
| if (toSend > *p_max_bytes) toSend = *p_max_bytes; |
|
|
switch(c->type) { | if (0 == toSend) { |
case MEM_CHUNK: { | chunkqueue_remove_finished_chunks(cq); |
char * offset; | return 0; |
off_t toSend; | } |
ssize_t r; | |
|
|
size_t num_chunks, i; | if (0 != network_open_file_chunk(srv, con, cq)) return -1; |
struct iovec chunks[UIO_MAXIOV]; | |
chunk *tc; | |
|
|
size_t num_bytes = 0; | fvec.sfv_fd = c->file.fd; |
| fvec.sfv_flag = 0; |
| fvec.sfv_off = offset; |
| fvec.sfv_len = toSend; |
|
|
/* we can't send more then SSIZE_MAX bytes in one chunk */ | /* Solaris sendfilev() */ |
|
|
/* build writev list | if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) { |
* | switch(errno) { |
* 1. limit: num_chunks < UIO_MAXIOV | case EAGAIN: |
* 2. limit: num_bytes < SSIZE_MAX | case EINTR: |
*/ | /* for EAGAIN/EINTR written still contains the sent bytes */ |
for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); | break; /* try again later */ |
| case EPIPE: |
for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { | case ENOTCONN: |
if (tc->mem->used == 0) { | return -2; |
chunks[i].iov_base = tc->mem->ptr; | case EINVAL: |
chunks[i].iov_len = 0; | case ENOSYS: |
} else { | #if defined(ENOTSUP) \ |
offset = tc->mem->ptr + tc->offset; | && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) |
toSend = tc->mem->used - 1 - tc->offset; | case ENOTSUP: |
| #endif |
chunks[i].iov_base = offset; | #ifdef EOPNOTSUPP |
| case EOPNOTSUPP: |
/* protect the return value of writev() */ | #endif |
if (toSend > max_bytes || | #ifdef ESOCKTNOSUPPORT |
(off_t) num_bytes + toSend > max_bytes) { | case ESOCKTNOSUPPORT: |
chunks[i].iov_len = max_bytes - num_bytes; | #endif |
| #ifdef EAFNOSUPPORT |
num_chunks = i + 1; | case EAFNOSUPPORT: |
break; | #endif |
} else { | #ifdef USE_MMAP |
chunks[i].iov_len = toSend; | 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); |
num_bytes += toSend; | #endif |
} | |
} | |
| |
if ((r = writev(fd, chunks, num_chunks)) < 0) { | |
switch (errno) { | |
case EAGAIN: | |
case EINTR: | |
r = 0; | |
break; | |
case EPIPE: | |
case ECONNRESET: | |
return -2; | |
default: | |
log_error_write(srv, __FILE__, __LINE__, "ssd", | |
"writev failed:", strerror(errno), fd); | |
| |
return -1; | |
} | |
} | |
| |
/* check which chunks have been written */ | |
cq->bytes_out += r; | |
| |
for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { | |
if (r >= (ssize_t)chunks[i].iov_len) { | |
/* written */ | |
r -= chunks[i].iov_len; | |
tc->offset += chunks[i].iov_len; | |
| |
if (chunk_finished) { | |
/* skip the chunks from further touches */ | |
c = c->next; | |
} else { | |
/* chunks_written + c = c->next is done in the for()*/ | |
chunk_finished = 1; | |
} | |
} else { | |
/* partially written */ | |
| |
tc->offset += r; | |
chunk_finished = 0; | |
| |
break; | |
} | |
} | |
| |
break; | |
} | |
case FILE_CHUNK: { | |
ssize_t r; | |
off_t offset, toSend; | |
size_t written; | |
sendfilevec_t fvec; | |
stat_cache_entry *sce = NULL; | |
int ifd; | |
| |
if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { | |
log_error_write(srv, __FILE__, __LINE__, "sb", | |
strerror(errno), c->file.name); | |
return -1; | |
} | |
| |
offset = c->file.start + c->offset; | |
toSend = c->file.length - c->offset; | |
if (toSend > max_bytes) toSend = max_bytes; | |
| |
if (offset > sce->st.st_size) { | |
log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); | |
| |
return -1; | |
} | |
| |
if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { | |
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); | |
| |
return -1; | |
} | |
| |
fvec.sfv_fd = ifd; | |
fvec.sfv_flag = 0; | |
fvec.sfv_off = offset; | |
fvec.sfv_len = toSend; | |
| |
/* Solaris sendfilev() */ | |
if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) { | |
if (errno != EAGAIN) { | |
log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); | |
| |
close(ifd); | |
return -1; | |
} | |
| |
r = 0; | |
} | |
| |
close(ifd); | |
c->offset += written; | |
cq->bytes_out += written; | |
max_bytes -= written; | |
| |
if (c->offset == c->file.length) { | |
chunk_finished = 1; | |
} | |
| |
break; | |
} | |
default: |
default: |
| log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); |
log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); | |
| |
return -1; |
return -1; |
} |
} |
|
} |
|
|
if (!chunk_finished) { | if (written >= 0) { |
/* not finished yet */ | chunkqueue_mark_written(cq, written); |
| *p_max_bytes -= written; |
break; | |
} | |
} |
} |
|
|
return 0; | return (r >= 0 && (off_t) written == toSend) ? 0 : -3; |
} |
} |
|
|
#endif | #endif /* USE_SOLARIS_SENDFILEV */ |