From: Willy Tarreau Date: Mon, 8 Oct 2012 18:11:03 +0000 (+0200) Subject: MEDIUM: listener: add support for linux's accept4() syscall X-Git-Tag: v1.5-dev13~180 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1bc4aab2902d732530ccbd098d30e519aab3abdd;p=thirdparty%2Fhaproxy.git MEDIUM: listener: add support for linux's accept4() syscall On Linux, accept4() does the same as accept() except that it allows the caller to specify some flags to set on the resulting socket. We use this to set the O_NONBLOCK flag and thus to save one fcntl() call in each connection. The effect is a small performance gain of around 1%. The option is automatically enabled when target linux2628 is set, or when the USE_ACCEPT4 Makefile variable is set. If the libc is too old to provide the equivalent function, this is automatically detected and our own function is used instead. In any case it is possible to force the use of our implementation with USE_MY_ACCEPT4. --- diff --git a/Makefile b/Makefile index 653c083cef..5652d59be5 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,8 @@ # USE_GETADDRINFO : use getaddrinfo() to resolve IPv6 host names. # USE_OPENSSL : enable use of OpenSSL. Recommended, but see below. # USE_FUTEX : enable use of futex on kernel 2.6. Automatic. +# USE_ACCEPT4 : enable use of accept4() on linux. Automatic. +# USE_MY_ACCEPT4 : use own implemention of accept4() if glibc < 2.10. # # Options can be forced by specifying "USE_xxx=1" or can be disabled by using # "USE_xxx=" (empty string). @@ -240,6 +242,7 @@ ifeq ($(TARGET),linux2628) USE_LIBCRYPT = implicit USE_LINUX_SPLICE= implicit USE_LINUX_TPROXY= implicit + USE_ACCEPT4 = implicit USE_FUTEX = implicit else ifeq ($(TARGET),solaris) @@ -439,6 +442,16 @@ OPTIONS_CFLAGS += -DUSE_MY_SPLICE BUILD_OPTIONS += $(call ignore_implicit,USE_MY_SPLICE) endif +ifneq ($(USE_ACCEPT4),) +OPTIONS_CFLAGS += -DUSE_ACCEPT4 +BUILD_OPTIONS += $(call ignore_implicit,USE_ACCEPT4) +endif + +ifneq ($(USE_MY_ACCEPT4),) +OPTIONS_CFLAGS += -DUSE_MY_ACCEPT4 +BUILD_OPTIONS += $(call ignore_implicit,USE_MY_ACCEPT4) +endif + ifneq ($(USE_NETFILTER),) OPTIONS_CFLAGS += -DNETFILTER BUILD_OPTIONS += $(call ignore_implicit,USE_NETFILTER) diff --git a/include/common/accept4.h b/include/common/accept4.h new file mode 100644 index 0000000000..9c81b80a97 --- /dev/null +++ b/include/common/accept4.h @@ -0,0 +1,72 @@ +/* + * include/common/accept4.h + * Definition of the accept4 system call for older Linux libc. + * + * Copyright 2000-2012 Willy Tarreau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _COMMON_ACCEPT4_H +#define _COMMON_ACCEPT4_H + +#if defined (__linux__) && defined(USE_ACCEPT4) + +#include +#include +#include +#include +#include +#include +#include + +/* On recent Linux kernels, the accept4() syscall may be used to avoid an fcntl() + * call to set O_NONBLOCK on the resulting socket. It was introduced in Linux + * 2.6.28 and is not present in older libcs. + */ +#ifndef SOCK_NONBLOCK +#define SOCK_NONBLOCK O_NONBLOCK +#endif + +#if defined(USE_MY_ACCEPT4) || !defined(SYS_ACCEPT4) +#if defined(CONFIG_HAP_LINUX_VSYSCALL) && defined(__linux__) && defined(__i386__) +/* The syscall is redefined somewhere else */ +extern int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); +#elif ACCEPT4_USE_SOCKETCALL +static int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) +{ + unsigned long args[4]; + static _syscall2(int, socketcall, int, call, unsigned long *, args); + + args[0] = (unsigned long)sockfd; + args[1] = (unsigned long)addr; + args[2] = (unsigned long)addrlen; + args[3] = (unsigned long)flags; + return socketcall(SYS_ACCEPT4, args); +} +#else +static _syscall4(int, accept4, int, sockfd, struct sockaddr *, addr, socklen_t *, addrlen, int, flags); +#endif /* VSYSCALL etc... */ +#endif /* USE_MY_ACCEPT4 */ +#endif /* __linux__ && USE_ACCEPT4 */ +#endif /* _COMMON_ACCEPT4_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/include/common/syscall.h b/include/common/syscall.h index a3b27eb168..3ccbf5a8db 100644 --- a/include/common/syscall.h +++ b/include/common/syscall.h @@ -2,7 +2,7 @@ * include/common/syscall.h * Redefinition of some missing OS-specific system calls. * - * Copyright 2000-2011 Willy Tarreau + * Copyright 2000-2012 Willy Tarreau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -124,6 +124,21 @@ #endif /* $arch */ #endif /* __NR_splice */ +/* accept4() appeared in Linux 2.6.28, but it might not be in all libcs. Some + * archs have it as a native syscall, other ones use the socketcall instead. + */ +#ifndef __NR_accept4 +#if defined(__x86_64__) +#define __NR_accept4 288 +#elif defined(__sparc__) || defined(__sparc64__) +#define __NR_splice 323 +#else +#define ACCEPT4_USE_SOCKETCALL 1 +#ifndef SYS_ACCEPT4 +#define SYS_ACCEPT4 18 +#endif /* SYS_ACCEPT4 */ +#endif /* $arch */ +#endif /* __NR_accept4 */ #endif /* __linux__ */ #endif /* _COMMON_SYSCALL_H */ diff --git a/src/i386-linux-vsys.c b/src/i386-linux-vsys.c index 1bdd9e9d7c..9d30334616 100644 --- a/src/i386-linux-vsys.c +++ b/src/i386-linux-vsys.c @@ -120,6 +120,10 @@ asm " mov $0x05, %eax\n" " jmp socketcall\n" + "accept4: .GLOBL accept4\n" + " mov $0x12, %eax\n" + " jmp socketcall\n" + "getsockname: .GLOBL getsockname\n" " mov $0x06, %eax\n" " jmp socketcall\n" diff --git a/src/listener.c b/src/listener.c index 7c21b9db2f..c4259bcd5a 100644 --- a/src/listener.c +++ b/src/listener.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -308,7 +309,11 @@ void listener_accept(int fd) return; } +#ifdef USE_ACCEPT4 + cfd = accept4(fd, (struct sockaddr *)&addr, &laddr, SOCK_NONBLOCK); +#else cfd = accept(fd, (struct sockaddr *)&addr, &laddr); +#endif if (unlikely(cfd == -1)) { switch (errno) { case EAGAIN: diff --git a/src/session.c b/src/session.c index b5dfd2bb92..3fcce844a0 100644 --- a/src/session.c +++ b/src/session.c @@ -126,9 +126,13 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr) goto out_free_session; } - /* Adjust some socket options */ +#ifndef USE_ACCEPT4 + /* Adjust some socket options if the connection was accepted by a plain + * accept() syscall. + */ if (unlikely(fcntl(cfd, F_SETFL, O_NONBLOCK) == -1)) goto out_free_session; +#endif /* monitor-net and health mode are processed immediately after TCP * connection rules. This way it's possible to block them, but they