On Wed, Sep 07, 2005 at 09:22:59AM +0400, Evgeniy Polyakov ([EMAIL PROTECTED])
wrote:
> On Tue, Sep 06, 2005 at 08:57:57PM -0700, David S. Miller ([EMAIL PROTECTED])
> wrote:
> > From: Evgeniy Polyakov <[EMAIL PROTECTED]>
> > Date: Fri, 2 Sep 2005 18:00:55 +0400
> >
> > > sock_sendfile() and generic_file_sendpage() were implemented
> > > and presented in the attached patch.
> > > Such methods allows to use sendfile() for any file descriptor <-> file
> > > descriptor usage, especially usefull it is in the case socket -> file,
> > > when there are no copy_from_user() cases when writing the data.
> >
> > I do not understand the socket sendfile() implementation, you
> > seem to just be looping back the data to recvmsg(). How does
> > this work?
>
> It does recvmsg(), but main purpose was to not copy this into userspace,
> when destination is file descriptor/socket.
> It does memcpy() unfortunately, but eliminating such a copy will require
> completely new system call, like recvfile(), which will first prepare a
> page, and then doing network receiving into it.
Using recv()/write() is about 5% slower in one thread and about 8-10%
slower using two threads compared to new sendfile() on 1+1 hyperthreaded
machine. Server uses sendfile.
recv()/write():
3m45.424s 659905536
3m50.352s 659905536
receiving sendfile():
3m29.432s 659905536
3m33.065s 659905536
Programs attached for intrested readers.
Eliminating data copying in generic_file_sendpage() requires
creating new system call recvfile(), which will grap page from file and
write directly into it. This can be done as second iteration on top
of presented approach.
--
Evgeniy Polyakov
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#define ulog_err(f, a...) fprintf(stderr, f ": %s [%d].\n", ##a,
strerror(errno), errno)
#define ulog(f, a...) fprintf(stderr, f, ##a)
int create_socket(char *addr, unsigned short port)
{
int s;
struct hostent *h;
struct sockaddr_in sa;
h = gethostbyname(addr);
if (!h) {
ulog_err("gethostbyname");
return -1;
}
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1) {
ulog_err("Failed to create a socket");
return -1;
}
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], 4);
if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
ulog_err("connect");
close(s);
return -1;
}
return s;
}
static ssize_t test_write(int in, int s, int count)
{
char *buf, *ptr;
int err, err1;
int sz = 4096;
ssize_t bytes = 0;
buf = malloc(sz);
if (!buf) {
ulog("Failed to allocate buffer of %d bytes.\n", count);
return -ENOMEM;
}
while (1) {
err = recv(s, buf, sz, 0);
if (err <= 0) {
ulog_err("recv");
break;
}
count -= err;
bytes += err;
ptr = buf;
while (err) {
err1 = write(in, ptr, err);
if (err1 <= 0)
break;
err -= err1;
ptr += err1;
}
}
free(buf);
return bytes;
}
static ssize_t test_sendfile(int in, int s, int count)
{
int err;
ssize_t bytes = 0;
while (1) {
err = sendfile(in, s, NULL, count);
if (err <= 0) {
ulog_err("sendfile");
break;
}
bytes += err;
}
return bytes;
}
int main(int argc, char *argv[])
{
int s, in;
size_t count = 1024*1024*1000;
int type;
ssize_t bytes;
if (argc < 5) {
ulog("Usage: %s ip port file type\n", argv[0]);
return -1;
}
type = atoi(argv[4]);
s = create_socket(argv[1], atoi(argv[2]));
if (s < 0)
return s;
in = open(argv[3], O_RDWR | O_TRUNC | O_CREAT, 0644);
if (in == -1) {
ulog_err("open");
return -1;
}
if (type == 0)
bytes = test_sendfile(in, s, count);
else
bytes = test_write(in, s, count);
ulog("%zd bytes.\n", bytes);
close(s);
close(in);
return 0;
}
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#define ulog_err(f, a...) fprintf(stderr, f ": %s [%d].\n", ##a,
strerror(errno), errno)
#define ulog(f, a...) fprintf(stderr, f, ##a)
int create_socket(char *addr, unsigned short port)
{
int s;
struct hostent *h;
struct sockaddr_in sa;
h = gethostbyname(addr);
if (!h) {
ulog_err("gethostbyname");
return -1;
}
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1) {
ulog_err("Failed to create a socket");
return -1;
}
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], 4);
if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
ulog_err("bind");
close(s);
return -1;
}
if (listen(s, 10)) {
ulog_err("Failed to make socket listen");
close(s);
return -1;
}
return s;
}
int main(int argc, char *argv[])
{
int s, in, err;
size_t count = 1024*1024*1000;
if (argc < 4) {
ulog("Usage: %s ip port file\n", argv[0]);
return -1;
}
s = create_socket(argv[1], atoi(argv[2]));
if (s < 0)
return s;
while (1) {
int cs;
struct sockaddr_in csin;
socklen_t len = sizeof(csin);
in = open(argv[3], O_RDONLY);
if (in == -1) {
ulog_err("open");
return -1;
}
cs = accept(s, (struct sockaddr *)&csin, &len);
if (cs == -1) {
ulog_err("accept");
close(in);
continue;
}
err = sendfile(cs, in, NULL, count);
if (err <= 0) {
ulog_err("sendfile");
close(in);
continue;
}
ulog("%d bytes were written.\n", err);
close(in);
close(cs);
}
close(s);
close(in);
return 0;
}