Bugzilla bug 132208: implemented PR_SendFile on Linux with the sendfile

system call and the TCP_CORK socket option.  r=jgmyers.
Modified Files: _linux.h unix_errors.c ptio.c
This commit is contained in:
wtc%netscape.com 2002-03-20 21:09:41 +00:00
Родитель a5d7e5a2e9
Коммит f643d5b33d
3 изменённых файлов: 177 добавлений и 0 удалений

Просмотреть файл

@ -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___ */

Просмотреть файл

@ -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 */

Просмотреть файл

@ -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