Speed up ftpd and make it more efficient:

- set TCP_NOPUSH to keep from sending short packets at each write(2) boundary
- set SO_SNDBUF to 64k so we have a reasonable amount of buffer space
- for a regular file in binary mode which is not being restarted and is
. smaller than 16 Meg, use mmap(2) and write(2) the whole file in one big
  gulp

In the most common circumstances, this should dramatically reduce the
system-call load from ftpd, since the call to write() will not return until
the entire file has been written, rather than writing just a few K at a time
in a loop.
This commit is contained in:
Garrett Wollman 1995-05-03 16:58:12 +00:00
parent 275a19fedc
commit 9fc5823a7a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=8240

View File

@ -49,10 +49,12 @@ static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#define FTP_NAMES
#include <arpa/ftp.h>
@ -187,7 +189,7 @@ static FILE *getdatasock __P((char *));
static char *gunique __P((char *));
static void lostconn __P((int));
static int receive_data __P((FILE *, FILE *));
static void send_data __P((FILE *, FILE *, off_t));
static void send_data __P((FILE *, FILE *, off_t, off_t, int));
static struct passwd *
sgetpwnam __P((char *));
static char *sgetsave __P((char *));
@ -739,7 +741,8 @@ retrieve(cmd, name)
#ifdef STATS
time(&start);
#endif
send_data(fin, dout, st.st_blksize);
send_data(fin, dout, st.st_blksize, st.st_size,
restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
#ifdef STATS
if (cmd == 0 && guest && stats)
logxfer( name, st.st_size, start);
@ -857,6 +860,23 @@ getdatasock(mode)
if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif
#ifdef TCP_NOPUSH
/*
* Turn off push flag to keep sender TCP from sending short packets
* at the boundaries of each write(). Should probably do a SO_SNDBUF
* to set the send buffer size as well, but that may not be desirable
* in heavy-load situations.
*/
on = 1;
if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
#endif
#ifdef SO_SNDBUF
on = 65536;
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0)
syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
#endif
return (fdopen(s, mode));
bad:
/* Return the real value of errno (close may change it) */
@ -941,17 +961,20 @@ dataconn(name, size, mode)
/*
* Tranfer the contents of "instr" to "outstr" peer using the appropriate
* encapsulation of the data subject * to Mode, Structure, and Type.
* encapsulation of the data subject to Mode, Structure, and Type.
*
* NB: Form isn't handled.
*/
static void
send_data(instr, outstr, blksize)
send_data(instr, outstr, blksize, filesize, isreg)
FILE *instr, *outstr;
off_t blksize;
off_t filesize;
int isreg;
{
int c, cnt, filefd, netfd;
char *buf;
char *buf, *bp;
size_t len;
transflag++;
if (setjmp(urgcatch)) {
@ -981,13 +1004,45 @@ send_data(instr, outstr, blksize)
case TYPE_I:
case TYPE_L:
/*
* isreg is only set if we are not doing restart and we
* are sending a regular file
*/
netfd = fileno(outstr);
filefd = fileno(instr);
if (isreg && filesize < (off_t)16 * 1024 * 1024) {
buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd,
(off_t)0);
if (!buf) {
syslog(LOG_WARNING, "mmap(%lu): %m",
(unsigned long)filesize);
goto oldway;
}
bp = buf;
len = filesize;
do {
cnt = write(netfd, bp, len);
len -= cnt;
bp += cnt;
if (cnt > 0) byte_count += cnt;
} while(cnt > 0 && len > 0);
transflag = 0;
munmap(buf, (size_t)filesize);
if (cnt < 0)
goto data_err;
reply(226, "Transfer complete.");
return;
}
oldway:
if ((buf = malloc((u_int)blksize)) == NULL) {
transflag = 0;
perror_reply(451, "Local resource failure: malloc");
return;
}
netfd = fileno(outstr);
filefd = fileno(instr);
while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
write(netfd, buf, cnt) == cnt)
byte_count += cnt;