diff options
author | wtc%netscape.com <devnull@localhost> | 2002-03-20 21:09:41 +0000 |
---|---|---|
committer | wtc%netscape.com <devnull@localhost> | 2002-03-20 21:09:41 +0000 |
commit | 6adcffd72bc6cb0954b957a9dac68e9e13a78d00 (patch) | |
tree | 1ac71e85d5948b49a4ffe15cd2430a17d3880dc2 | |
parent | acb675f6f1e07b7f7f26df9cdb235d2ff1446e4d (diff) | |
download | nspr-hg-6adcffd72bc6cb0954b957a9dac68e9e13a78d00.tar.gz |
Bugzilla bug 132208: implemented PR_SendFile on Linux with the sendfileNSPR_4_2_RC1
system call and the TCP_CORK socket option. r=jgmyers.
Modified Files: _linux.h unix_errors.c ptio.c
-rw-r--r-- | pr/include/md/_linux.h | 2 | ||||
-rw-r--r-- | pr/src/md/unix/unix_errors.c | 7 | ||||
-rw-r--r-- | pr/src/pthreads/ptio.c | 168 |
3 files changed, 177 insertions, 0 deletions
diff --git a/pr/include/md/_linux.h b/pr/include/md/_linux.h index 86ea248c..3f4b6735 100644 --- a/pr/include/md/_linux.h +++ b/pr/include/md/_linux.h @@ -470,4 +470,6 @@ extern int __syscall_poll(struct pollfd *ufds, unsigned long int nfds, /* For writev() */ #include <sys/uio.h> +extern void _MD_linux_map_sendfile_error(int err); + #endif /* nspr_linux_defs_h___ */ diff --git a/pr/src/md/unix/unix_errors.c b/pr/src/md/unix/unix_errors.c index 1ce52e68..15b423cb 100644 --- a/pr/src/md/unix/unix_errors.c +++ b/pr/src/md/unix/unix_errors.c @@ -847,3 +847,10 @@ void _MD_solaris_map_sendfile_error(int err) _MD_unix_map_default_error(err) ; } #endif /* SOLARIS */ + +#ifdef LINUX +void _MD_linux_map_sendfile_error(int err) +{ + _MD_unix_map_default_error(err) ; +} +#endif /* LINUX */ diff --git a/pr/src/pthreads/ptio.c b/pr/src/pthreads/ptio.c index 697a9273..a7fde447 100644 --- a/pr/src/pthreads/ptio.c +++ b/pr/src/pthreads/ptio.c @@ -173,11 +173,21 @@ static ssize_t (*pt_aix_sendfile_fptr)() = NULL; #endif /* HAVE_SEND_FILE */ #endif /* AIX */ +#ifdef LINUX +#include <sys/sendfile.h> +#endif + #include "primpl.h" /* On Alpha Linux, these are already defined in sys/socket.h */ #if !(defined(LINUX) && defined(__alpha)) #include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ +#ifdef LINUX +/* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */ +#ifndef TCP_CORK +#define TCP_CORK 3 +#endif +#endif #endif #if defined(SOLARIS) @@ -338,6 +348,15 @@ struct pt_Continuation */ int nbytes_to_send; /* size of header and file */ #endif /* SOLARIS */ + +#ifdef LINUX + /* + * For sendfile() + */ + int in_fd; /* descriptor of file to send */ + off_t offset; + size_t count; +#endif /* LINUX */ PRIntervalTime timeout; /* client (relative) timeout */ @@ -1059,6 +1078,33 @@ static PRBool pt_solaris_sendfile_cont(pt_Continuation *op, PRInt16 revents) } #endif /* SOLARIS */ +#ifdef LINUX +static PRBool pt_linux_sendfile_cont(pt_Continuation *op, PRInt16 revents) +{ + ssize_t rv; + off_t oldoffset; + + oldoffset = op->offset; + rv = sendfile(op->arg1.osfd, op->in_fd, &op->offset, op->count); + op->syserrno = errno; + + if (rv == -1) { + if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { + op->result.code = -1; + return PR_TRUE; + } + rv = 0; + } + PR_ASSERT(rv == op->offset - oldoffset); + op->result.code += rv; + if (rv < op->count) { + op->count -= rv; + return PR_FALSE; + } + return PR_TRUE; +} +#endif /* LINUX */ + void _PR_InitIO(void) { #if defined(DEBUG) @@ -2427,6 +2473,126 @@ static PRInt32 pt_SolarisDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, #endif /* SOLARIS */ +#ifdef LINUX +/* + * pt_LinuxSendFile + * + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in Linux kernel 2.2 or higher. + */ + +static PRInt32 pt_LinuxSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + struct stat statbuf; + size_t file_nbytes_to_send; + PRInt32 count = 0; + ssize_t rv; + int syserrno; + off_t offset; + PRBool tcp_cork_enabled = PR_FALSE; + int tcp_cork; + + if (sfd->file_nbytes == 0) { + /* Get file size */ + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + file_nbytes_to_send = statbuf.st_size - sfd->file_offset; + } else { + file_nbytes_to_send = sfd->file_nbytes; + } + + if (sfd->hlen != 0 || sfd->tlen != 0) { + tcp_cork = 1; + if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, + &tcp_cork, sizeof tcp_cork) == -1) { + _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + return -1; + } + tcp_cork_enabled = PR_TRUE; + } + + if (sfd->hlen != 0) { + count = PR_Send(sd, sfd->header, sfd->hlen, 0, timeout); + if (count == -1) { + goto failed; + } + } + + if (file_nbytes_to_send != 0) { + offset = sfd->file_offset; + do { + rv = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, + &offset, file_nbytes_to_send); + } while (rv == -1 && (syserrno = errno) == EINTR); + if (rv == -1) { + if (syserrno != EAGAIN && syserrno != EWOULDBLOCK) { + _MD_linux_map_sendfile_error(syserrno); + count = -1; + goto failed; + } + rv = 0; + } + PR_ASSERT(rv == offset - sfd->file_offset); + count += rv; + + if (rv < file_nbytes_to_send) { + pt_Continuation op; + + op.arg1.osfd = sd->secret->md.osfd; + op.in_fd = sfd->fd->secret->md.osfd; + op.offset = offset; + op.count = file_nbytes_to_send - rv; + op.result.code = count; + op.timeout = timeout; + op.function = pt_linux_sendfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + if (count == -1) { + _MD_linux_map_sendfile_error(syserrno); + goto failed; + } + } + } + + if (sfd->tlen != 0) { + rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); + if (rv == -1) { + count = -1; + goto failed; + } + count += rv; + } + +failed: + if (tcp_cork_enabled) { + tcp_cork = 0; + if (setsockopt(sd->secret->md.osfd, SOL_TCP, TCP_CORK, + &tcp_cork, sizeof tcp_cork) == -1 && count != -1) { + _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + count = -1; + } + } + if (count != -1) { + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + PR_ASSERT(count == sfd->hlen + sfd->tlen + file_nbytes_to_send); + } + return count; +} +#endif /* LINUX */ + #ifdef AIX extern int _pr_aix_send_file_use_disabled; #endif @@ -2467,6 +2633,8 @@ static PRInt32 pt_SendFile( #else return(pt_SolarisDispatchSendFile(sd, sfd, flags, timeout)); #endif /* HAVE_SENDFILEV */ +#elif defined(LINUX) + return(pt_LinuxSendFile(sd, sfd, flags, timeout)); #else return(PR_EmulateSendFile(sd, sfd, flags, timeout)); #endif |