MAJOR: polling: replace epoll with sepoll and remove sepoll

Now that all pollers make use of speculative I/O, there is no point
having two epoll implementations, so replace epoll with the sepoll code
and remove sepoll which has just become the standard epoll method.
This commit is contained in:
Willy Tarreau 2012-11-11 17:42:00 +01:00
parent 4a22627672
commit e9f49e78fe
10 changed files with 159 additions and 611 deletions

View File

@ -17,7 +17,6 @@
# USE_POLL : enable poll(). Automatic. # USE_POLL : enable poll(). Automatic.
# USE_PRIVATE_CACHE : disable shared memory cache of ssl sessions. # USE_PRIVATE_CACHE : disable shared memory cache of ssl sessions.
# USE_REGPARM : enable regparm optimization. Recommended on x86. # USE_REGPARM : enable regparm optimization. Recommended on x86.
# USE_SEPOLL : enable speculative epoll(). Automatic.
# USE_STATIC_PCRE : enable static libpcre. Recommended. # USE_STATIC_PCRE : enable static libpcre. Recommended.
# USE_TPROXY : enable transparent proxy. Automatic. # USE_TPROXY : enable transparent proxy. Automatic.
# USE_LINUX_TPROXY : enable full transparent proxy. Automatic. # USE_LINUX_TPROXY : enable full transparent proxy. Automatic.
@ -216,7 +215,6 @@ ifeq ($(TARGET),linux24e)
USE_NETFILTER = implicit USE_NETFILTER = implicit
USE_POLL = implicit USE_POLL = implicit
USE_EPOLL = implicit USE_EPOLL = implicit
USE_SEPOLL = implicit
USE_MY_EPOLL = implicit USE_MY_EPOLL = implicit
USE_TPROXY = implicit USE_TPROXY = implicit
USE_LIBCRYPT = implicit USE_LIBCRYPT = implicit
@ -227,7 +225,6 @@ ifeq ($(TARGET),linux26)
USE_NETFILTER = implicit USE_NETFILTER = implicit
USE_POLL = implicit USE_POLL = implicit
USE_EPOLL = implicit USE_EPOLL = implicit
USE_SEPOLL = implicit
USE_TPROXY = implicit USE_TPROXY = implicit
USE_LIBCRYPT = implicit USE_LIBCRYPT = implicit
USE_FUTEX = implicit USE_FUTEX = implicit
@ -238,7 +235,6 @@ ifeq ($(TARGET),linux2628)
USE_NETFILTER = implicit USE_NETFILTER = implicit
USE_POLL = implicit USE_POLL = implicit
USE_EPOLL = implicit USE_EPOLL = implicit
USE_SEPOLL = implicit
USE_TPROXY = implicit USE_TPROXY = implicit
USE_LIBCRYPT = implicit USE_LIBCRYPT = implicit
USE_LINUX_SPLICE= implicit USE_LINUX_SPLICE= implicit
@ -424,12 +420,6 @@ OPTIONS_OBJS += src/ev_epoll.o
BUILD_OPTIONS += $(call ignore_implicit,USE_EPOLL) BUILD_OPTIONS += $(call ignore_implicit,USE_EPOLL)
endif endif
ifneq ($(USE_SEPOLL),)
OPTIONS_CFLAGS += -DENABLE_SEPOLL
OPTIONS_OBJS += src/ev_sepoll.o
BUILD_OPTIONS += $(call ignore_implicit,USE_SEPOLL)
endif
ifneq ($(USE_MY_EPOLL),) ifneq ($(USE_MY_EPOLL),)
OPTIONS_CFLAGS += -DUSE_MY_EPOLL OPTIONS_CFLAGS += -DUSE_MY_EPOLL
BUILD_OPTIONS += $(call ignore_implicit,USE_MY_EPOLL) BUILD_OPTIONS += $(call ignore_implicit,USE_MY_EPOLL)

View File

@ -461,7 +461,6 @@ The following keywords are supported in the "global" section :
- noepoll - noepoll
- nokqueue - nokqueue
- nopoll - nopoll
- nosepoll
- nosplice - nosplice
- spread-checks - spread-checks
- tune.bufsize - tune.bufsize
@ -708,7 +707,7 @@ maxzlibmem <number>
noepoll noepoll
Disables the use of the "epoll" event polling system on Linux. It is Disables the use of the "epoll" event polling system on Linux. It is
equivalent to the command-line argument "-de". The next polling system equivalent to the command-line argument "-de". The next polling system
used will generally be "poll". See also "nosepoll", and "nopoll". used will generally be "poll". See also "nopoll".
nokqueue nokqueue
Disables the use of the "kqueue" event polling system on BSD. It is Disables the use of the "kqueue" event polling system on BSD. It is
@ -719,13 +718,7 @@ nopoll
Disables the use of the "poll" event polling system. It is equivalent to the Disables the use of the "poll" event polling system. It is equivalent to the
command-line argument "-dp". The next polling system used will be "select". command-line argument "-dp". The next polling system used will be "select".
It should never be needed to disable "poll" since it's available on all It should never be needed to disable "poll" since it's available on all
platforms supported by HAProxy. See also "nosepoll", and "nopoll" and platforms supported by HAProxy. See also "nokqueue" and "noepoll".
"nokqueue".
nosepoll
Disables the use of the "speculative epoll" event polling system on Linux. It
is equivalent to the command-line argument "-ds". The next polling system
used will generally be "epoll". See also "nosepoll", and "nopoll".
nosplice nosplice
Disables the use of kernel tcp splicing between sockets on Linux. It is Disables the use of kernel tcp splicing between sockets on Linux. It is

View File

@ -62,7 +62,6 @@ There are only a few command line options :
-s shows statistics (only if compiled in) -s shows statistics (only if compiled in)
-l shows even more statistics (implies '-s') -l shows even more statistics (implies '-s')
-dk disables use of kqueue() -dk disables use of kqueue()
-ds disables use of speculative epoll()
-de disables use of epoll() -de disables use of epoll()
-dp disables use of poll() -dp disables use of poll()
-db disables background mode (stays in foreground, useful for debugging) -db disables background mode (stays in foreground, useful for debugging)
@ -130,7 +129,6 @@ the following ones :
- daemon - daemon
- debug - debug
- nokqueue - nokqueue
- nosepoll
- noepoll - noepoll
- nopoll - nopoll
- quiet - quiet
@ -357,8 +355,8 @@ slightly save CPU cycles in presence of large number of connections.
Haproxy will use kqueue() or speculative epoll() when available, then epoll(), Haproxy will use kqueue() or speculative epoll() when available, then epoll(),
and will fall back to poll(), then to select(). However, if for any reason you and will fall back to poll(), then to select(). However, if for any reason you
need to disable epoll() or poll() (eg. because of a bug or just to compare need to disable epoll() or poll() (eg. because of a bug or just to compare
performance), new global options have been created for this matter : 'nosepoll', performance), new global options have been created for this matter : 'nopoll',
'nokqueue', 'noepoll' and 'nopoll'. 'nokqueue', and 'noepoll'.
Example : Example :
--------- ---------
@ -378,7 +376,7 @@ time.
To make debugging easier, the '-de' runtime argument disables epoll support, To make debugging easier, the '-de' runtime argument disables epoll support,
the '-dp' argument disables poll support, '-dk' disables kqueue and '-ds' the '-dp' argument disables poll support, '-dk' disables kqueue and '-ds'
disables speculative epoll(). They are respectively equivalent to 'noepoll', disables speculative epoll(). They are respectively equivalent to 'noepoll',
'nopoll', 'nokqueue' and 'nosepoll'. 'nopoll', and 'nokqueue'.
2) Declaration of a listening service 2) Declaration of a listening service

View File

@ -137,7 +137,6 @@ support
- daemon - daemon
- debug - debug
- nokqueue - nokqueue
- nosepoll
- noepoll - noepoll
- nopoll - nopoll
- quiet - quiet
@ -384,7 +383,7 @@ puis epoll(), et se repliera sur poll(), puis en dernier lieu sur select().
Cependant, si pour une raison quelconque il s'avérait nécessaire de désactiver Cependant, si pour une raison quelconque il s'avérait nécessaire de désactiver
epoll() ou poll() (p.ex: à cause d'un bug ou juste pour comparer les epoll() ou poll() (p.ex: à cause d'un bug ou juste pour comparer les
performances), de nouvelles options globales ont été ajoutées dans ce but : performances), de nouvelles options globales ont été ajoutées dans ce but :
'nosepoll', 'nokqueue', 'noepoll' et 'nopoll'. 'nokqueue', 'noepoll' et 'nopoll'.
Exemple : Exemple :
--------- ---------
@ -403,7 +402,7 @@ activ
Afin de simplifier la résolution de problèmes, le paramètre '-de' en ligne de Afin de simplifier la résolution de problèmes, le paramètre '-de' en ligne de
commande désactive epoll(), le paramètre '-dp' désactive poll(), '-dk' kqueue() commande désactive epoll(), le paramètre '-dp' désactive poll(), '-dk' kqueue()
et '-ds' désactive speculative epoll(). Ils sont respectivement équivalents à et '-ds' désactive speculative epoll(). Ils sont respectivement équivalents à
'noepoll', 'nopoll', 'nokqueue' et 'nosepoll'. 'noepoll', 'nopoll', et 'nokqueue'.
2) Définition d'un service en écoute 2) Définition d'un service en écoute

View File

@ -2,7 +2,7 @@
* include/common/epoll.h * include/common/epoll.h
* epoll definitions for older libc. * epoll definitions for older libc.
* *
* Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu * Copyright (C) 2000-2012 Willy Tarreau - w@1wt.eu
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -29,7 +29,7 @@
#ifndef _COMMON_EPOLL_H #ifndef _COMMON_EPOLL_H
#define _COMMON_EPOLL_H #define _COMMON_EPOLL_H
#if defined (__linux__) && (defined(ENABLE_EPOLL) || defined(ENABLE_SEPOLL)) #if defined (__linux__) && defined(ENABLE_EPOLL)
#ifndef USE_MY_EPOLL #ifndef USE_MY_EPOLL
#include <sys/epoll.h> #include <sys/epoll.h>
@ -94,7 +94,7 @@ static inline _syscall4 (int, epoll_wait, int, epfd, struct epoll_event *, event
#endif /* USE_MY_EPOLL */ #endif /* USE_MY_EPOLL */
#endif /* __linux__ && (ENABLE_EPOLL || ENABLE_SEPOLL) */ #endif /* __linux__ && ENABLE_EPOLL */
#endif /* _COMMON_EPOLL_H */ #endif /* _COMMON_EPOLL_H */

View File

@ -2,7 +2,7 @@
* include/types/global.h * include/types/global.h
* Global variables. * Global variables.
* *
* Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu * Copyright (C) 2000-2012 Willy Tarreau - w@1wt.eu
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -54,9 +54,8 @@
#define GTUNE_USE_POLL (1<<1) #define GTUNE_USE_POLL (1<<1)
#define GTUNE_USE_EPOLL (1<<2) #define GTUNE_USE_EPOLL (1<<2)
#define GTUNE_USE_KQUEUE (1<<3) #define GTUNE_USE_KQUEUE (1<<3)
#define GTUNE_USE_SEPOLL (1<<4)
/* platform-specific options */ /* platform-specific options */
#define GTUNE_USE_SPLICE (1<<5) #define GTUNE_USE_SPLICE (1<<4)
/* Access level for a stats socket */ /* Access level for a stats socket */
#define ACCESS_LVL_NONE 0 #define ACCESS_LVL_NONE 0

View File

@ -514,9 +514,6 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
else if (!strcmp(args[0], "noepoll")) { else if (!strcmp(args[0], "noepoll")) {
global.tune.options &= ~GTUNE_USE_EPOLL; global.tune.options &= ~GTUNE_USE_EPOLL;
} }
else if (!strcmp(args[0], "nosepoll")) {
global.tune.options &= ~GTUNE_USE_SEPOLL;
}
else if (!strcmp(args[0], "nokqueue")) { else if (!strcmp(args[0], "nokqueue")) {
global.tune.options &= ~GTUNE_USE_KQUEUE; global.tune.options &= ~GTUNE_USE_KQUEUE;
} }

View File

@ -1,13 +1,12 @@
/* /*
* FD polling functions for linux epoll() * FD polling functions for Linux epoll
* *
* Copyright 2000-2011 Willy Tarreau <w@1wt.eu> * Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*
*/ */
#include <unistd.h> #include <unistd.h>
@ -16,37 +15,21 @@
#include <common/compat.h> #include <common/compat.h>
#include <common/config.h> #include <common/config.h>
#include <common/debug.h>
#include <common/epoll.h> #include <common/epoll.h>
#include <common/standard.h> #include <common/standard.h>
#include <common/ticks.h> #include <common/ticks.h>
#include <common/time.h> #include <common/time.h>
#include <common/tools.h> #include <common/tools.h>
#include <types/fd.h>
#include <types/global.h> #include <types/global.h>
#include <proto/fd.h>
#include <proto/signal.h> #include <proto/signal.h>
#include <proto/task.h> #include <proto/task.h>
/* This is what we store in a list. It consists in old values and fds to detect changes. */
struct fd_chg {
unsigned int prev:2; // previous state mask. New one is in fd_evts.
unsigned int fd:30; // file descriptor
};
static int nbchanges = 0; // number of changes pending static int absmaxevents = 0; // absolute maximum amounts of polled events
static struct fd_chg *chg_list = NULL; // list of changes
static struct fd_chg **chg_ptr = NULL; // per-fd changes
/* Each 32-bit word contains 2-bit descriptors of the latest state for 16 FDs :
* desc = (u32 >> (2*fd)) & 3
* desc = 0 : FD not set
* 1 : WRITE not set, READ set
* 2 : WRITE set, READ not set
* 3 : WRITE set, READ set
*/
static uint32_t *fd_evts;
/* private data */ /* private data */
static struct epoll_event *epoll_events; static struct epoll_event *epoll_events;
@ -57,183 +40,93 @@ static int epoll_fd;
*/ */
static struct epoll_event ev; static struct epoll_event ev;
/* converts a direction to a single bitmask.
* 0 => 1
* 1 => 2
*/
#define DIR2MSK(dir) ((dir) + 1)
/* converts an FD to an fd_evts offset and to a bit shift */
#define FD2OFS(fd) ((uint32_t)(fd) >> 4)
#define FD2BIT(fd) (((uint32_t)(fd) & 0xF) << 1)
#define FD2MSK(fd) (3 << FD2BIT(fd))
/* /*
* Returns non-zero if direction <dir> is already set for <fd>. * speculative epoll() poller
*/
REGPRM2 static int __fd_is_set(const int fd, int dir)
{
return (fd_evts[FD2OFS(fd)] >> FD2BIT(fd)) & DIR2MSK(dir);
}
/*
* Adds, mods or deletes <fd> according to current status and to new desired
* mask <dmask> :
*
* 0 = nothing
* 1 = EPOLLIN
* 2 = EPOLLOUT
* 3 = EPOLLIN | EPOLLOUT
*
*/
static int dmsk2event[4] = { 0, EPOLLIN, EPOLLOUT, EPOLLIN | EPOLLOUT };
REGPRM2 static void fd_flush_changes()
{
uint32_t ofs;
int opcode;
int prev, next;
int chg, fd;
for (chg = 0; chg < nbchanges; chg++) {
prev = chg_list[chg].prev;
fd = chg_list[chg].fd;
chg_ptr[fd] = NULL;
ofs = FD2OFS(fd);
next = (fd_evts[ofs] >> FD2BIT(fd)) & 3;
if (prev == next)
/* if the value was unchanged, do nothing */
continue;
ev.events = dmsk2event[next];
ev.data.fd = fd;
if (prev) {
if (!next) {
/* we want to delete it now */
opcode = EPOLL_CTL_DEL;
} else {
/* we want to switch it */
opcode = EPOLL_CTL_MOD;
}
} else {
/* the FD did not exist, let's add it */
opcode = EPOLL_CTL_ADD;
}
epoll_ctl(epoll_fd, opcode, fd, &ev);
}
nbchanges = 0;
}
REGPRM2 static void alloc_chg_list(const int fd, int old_evt)
{
struct fd_chg *ptr;
if (unlikely(chg_ptr[fd] != NULL))
return;
#if LIMIT_NUMBER_OF_CHANGES
if (nbchanges > 2)
fd_flush_changes();
#endif
ptr = &chg_list[nbchanges++];
chg_ptr[fd] = ptr;
ptr->fd = fd;
ptr->prev = old_evt;
}
REGPRM2 static void __fd_set(const int fd, int dir)
{
uint32_t ofs = FD2OFS(fd);
uint32_t dmsk = DIR2MSK(dir);
uint32_t old_evt;
old_evt = fd_evts[ofs] >> FD2BIT(fd);
old_evt &= 3;
if (unlikely(old_evt & dmsk))
return;
alloc_chg_list(fd, old_evt);
dmsk <<= FD2BIT(fd);
fd_evts[ofs] |= dmsk;
}
REGPRM2 static void __fd_clr(const int fd, int dir)
{
uint32_t ofs = FD2OFS(fd);
uint32_t dmsk = DIR2MSK(dir);
uint32_t old_evt;
old_evt = fd_evts[ofs] >> FD2BIT(fd);
old_evt &= 3;
if (unlikely(!(old_evt & dmsk)))
return;
alloc_chg_list(fd, old_evt);
dmsk <<= FD2BIT(fd);
fd_evts[ofs] &= ~dmsk;
}
REGPRM1 static void __fd_rem(int fd)
{
uint32_t ofs = FD2OFS(fd);
uint32_t old_evt;
old_evt = fd_evts[ofs] >> FD2BIT(fd);
old_evt &= 3;
if (unlikely(!old_evt))
return;
alloc_chg_list(fd, old_evt);
fd_evts[ofs] &= ~FD2MSK(fd);
}
/*
* On valid epoll() implementations, a call to close() automatically removes
* the fds. This means that the FD will appear as previously unset.
*/
REGPRM1 static void __fd_clo(int fd)
{
struct fd_chg *ptr;
fd_evts[FD2OFS(fd)] &= ~FD2MSK(fd);
ptr = chg_ptr[fd];
if (ptr) {
ptr->prev = 0;
chg_ptr[fd] = NULL;
}
}
/*
* epoll() poller
*/ */
REGPRM2 static void _do_poll(struct poller *p, int exp) REGPRM2 static void _do_poll(struct poller *p, int exp)
{ {
int status; int status, eo, en;
int fd; int fd, opcode;
int count; int count;
int updt_idx;
int wait_time; int wait_time;
if (likely(nbchanges)) /* first, scan the update list to find changes */
fd_flush_changes(); for (updt_idx = 0; updt_idx < fd_nbupdt; updt_idx++) {
fd = fd_updt[updt_idx];
en = fdtab[fd].spec_e & 15; /* new events */
eo = fdtab[fd].spec_e >> 4; /* previous events */
/* now let's wait for events */ if (fdtab[fd].owner && (eo ^ en)) {
if (run_queue || signal_queue_len) if ((eo ^ en) & FD_EV_POLLED_RW) {
wait_time = 0; /* poll status changed */
else if (!exp) if ((en & FD_EV_POLLED_RW) == 0) {
wait_time = MAX_DELAY_MS; /* fd removed from poll list */
else if (tick_is_expired(exp, now_ms)) opcode = EPOLL_CTL_DEL;
wait_time = 0; }
else { else if ((eo & FD_EV_POLLED_RW) == 0) {
wait_time = TICKS_TO_MS(tick_remain(now_ms, exp)) + 1; /* new fd in the poll list */
if (wait_time > MAX_DELAY_MS) opcode = EPOLL_CTL_ADD;
wait_time = MAX_DELAY_MS; }
else {
/* fd status changed */
opcode = EPOLL_CTL_MOD;
}
/* construct the epoll events based on new state */
ev.events = 0;
if (en & FD_EV_POLLED_R)
ev.events |= EPOLLIN;
if (en & FD_EV_POLLED_W)
ev.events |= EPOLLOUT;
ev.data.fd = fd;
epoll_ctl(epoll_fd, opcode, fd, &ev);
}
fdtab[fd].spec_e = (en << 4) + en; /* save new events */
if (!(en & FD_EV_ACTIVE_RW)) {
/* This fd doesn't use any active entry anymore, we can
* kill its entry.
*/
release_spec_entry(fd);
}
else if ((en & ~eo) & FD_EV_ACTIVE_RW) {
/* we need a new spec entry now */
alloc_spec_entry(fd);
}
}
fdtab[fd].updated = 0;
fdtab[fd].new = 0;
} }
fd_nbupdt = 0;
/* compute the epoll_wait() timeout */
if (fd_nbspec || run_queue || signal_queue_len) {
/* Maybe we still have events in the spec list, or there are
* some tasks left pending in the run_queue, so we must not
* wait in epoll() otherwise we would delay their delivery by
* the next timeout.
*/
wait_time = 0;
}
else {
if (!exp)
wait_time = MAX_DELAY_MS;
else if (tick_is_expired(exp, now_ms))
wait_time = 0;
else {
wait_time = TICKS_TO_MS(tick_remain(now_ms, exp)) + 1;
if (wait_time > MAX_DELAY_MS)
wait_time = MAX_DELAY_MS;
}
}
/* now let's wait for polled events */
fd = MIN(maxfd, global.tune.maxpollevents); fd = MIN(maxfd, global.tune.maxpollevents);
gettimeofday(&before_poll, NULL); gettimeofday(&before_poll, NULL);
@ -241,6 +134,8 @@ REGPRM2 static void _do_poll(struct poller *p, int exp)
tv_update_date(wait_time, status); tv_update_date(wait_time, status);
measure_idle(); measure_idle();
/* process polled events */
for (count = 0; count < status; count++) { for (count = 0; count < status; count++) {
int e = epoll_events[count].events; int e = epoll_events[count].events;
fd = epoll_events[count].data.fd; fd = epoll_events[count].data.fd;
@ -259,53 +154,84 @@ REGPRM2 static void _do_poll(struct poller *p, int exp)
((e & EPOLLERR) ? FD_POLL_ERR : 0) | ((e & EPOLLERR) ? FD_POLL_ERR : 0) |
((e & EPOLLHUP) ? FD_POLL_HUP : 0); ((e & EPOLLHUP) ? FD_POLL_HUP : 0);
if (fdtab[fd].iocb && fdtab[fd].owner && fdtab[fd].ev) if (fdtab[fd].iocb && fdtab[fd].owner && fdtab[fd].ev) {
int new_updt, old_updt = fd_nbupdt; /* Save number of updates to detect creation of new FDs. */
/* Mark the events as speculative before processing
* them so that if nothing can be done we don't need
* to poll again.
*/
if (fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP|FD_POLL_ERR))
fd_ev_set(fd, DIR_RD);
if (fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR))
fd_ev_set(fd, DIR_WR);
fdtab[fd].iocb(fd); fdtab[fd].iocb(fd);
/* One or more fd might have been created during the iocb().
* This mainly happens with new incoming connections that have
* just been accepted, so we'd like to process them immediately
* for better efficiency. Second benefit, if at the end the fds
* are disabled again, we can safely destroy their update entry
* to reduce the scope of later scans. This is the reason we
* scan the new entries backwards.
*/
for (new_updt = fd_nbupdt; new_updt > old_updt; new_updt--) {
fd = fd_updt[new_updt - 1];
if (!fdtab[fd].new)
continue;
fdtab[fd].new = 0;
fdtab[fd].ev &= FD_POLL_STICKY;
if ((fdtab[fd].spec_e & FD_EV_STATUS_R) == FD_EV_ACTIVE_R)
fdtab[fd].ev |= FD_POLL_IN;
if ((fdtab[fd].spec_e & FD_EV_STATUS_W) == FD_EV_ACTIVE_W)
fdtab[fd].ev |= FD_POLL_OUT;
if (fdtab[fd].ev && fdtab[fd].iocb && fdtab[fd].owner)
fdtab[fd].iocb(fd);
/* we can remove this update entry if it's the last one and is
* unused, otherwise we don't touch anything.
*/
if (new_updt == fd_nbupdt && fdtab[fd].spec_e == 0) {
fdtab[fd].updated = 0;
fd_nbupdt--;
}
}
}
} }
/* the caller will take care of speculative events */
} }
/* /*
* Initialization of the epoll() poller. * Initialization of the speculative epoll() poller.
* Returns 0 in case of failure, non-zero in case of success. If it fails, it * Returns 0 in case of failure, non-zero in case of success. If it fails, it
* disables the poller by setting its pref to 0. * disables the poller by setting its pref to 0.
*/ */
REGPRM1 static int _do_init(struct poller *p) REGPRM1 static int _do_init(struct poller *p)
{ {
__label__ fail_chg_ptr, fail_chg_list, fail_fdevt, fail_ee, fail_fd;
int fd_set_bytes;
p->private = NULL; p->private = NULL;
fd_set_bytes = 4 * (global.maxsock + 15) / 16;
epoll_fd = epoll_create(global.maxsock + 1); epoll_fd = epoll_create(global.maxsock + 1);
if (epoll_fd < 0) if (epoll_fd < 0)
goto fail_fd; goto fail_fd;
/* See comments at the top of the file about this formula. */
absmaxevents = MAX(global.tune.maxpollevents, global.maxsock);
epoll_events = (struct epoll_event*) epoll_events = (struct epoll_event*)
calloc(1, sizeof(struct epoll_event) * global.tune.maxpollevents); calloc(1, sizeof(struct epoll_event) * absmaxevents);
if (epoll_events == NULL) if (epoll_events == NULL)
goto fail_ee; goto fail_ee;
if ((fd_evts = (uint32_t *)calloc(1, fd_set_bytes)) == NULL)
goto fail_fdevt;
chg_list = (struct fd_chg *)calloc(1, sizeof(struct fd_chg) * global.maxsock);
if (chg_list == NULL)
goto fail_chg_list;
chg_ptr = (struct fd_chg **)calloc(1, sizeof(struct fd_chg*) * global.maxsock);
if (chg_ptr == NULL)
goto fail_chg_ptr;
return 1; return 1;
fail_chg_ptr:
free(chg_list);
fail_chg_list:
free(fd_evts);
fail_fdevt:
free(epoll_events);
fail_ee: fail_ee:
close(epoll_fd); close(epoll_fd);
epoll_fd = -1; epoll_fd = -1;
@ -315,16 +241,11 @@ REGPRM1 static int _do_init(struct poller *p)
} }
/* /*
* Termination of the epoll() poller. * Termination of the speculative epoll() poller.
* Memory is released and the poller is marked as unselectable. * Memory is released and the poller is marked as unselectable.
*/ */
REGPRM1 static void _do_term(struct poller *p) REGPRM1 static void _do_term(struct poller *p)
{ {
fd_flush_changes();
free(chg_ptr);
free(chg_list);
free(fd_evts);
free(epoll_events); free(epoll_events);
if (epoll_fd >= 0) { if (epoll_fd >= 0) {
@ -332,11 +253,7 @@ REGPRM1 static void _do_term(struct poller *p)
epoll_fd = -1; epoll_fd = -1;
} }
chg_ptr = NULL;
chg_list = NULL;
fd_evts = NULL;
epoll_events = NULL; epoll_events = NULL;
p->private = NULL; p->private = NULL;
p->pref = 0; p->pref = 0;
} }
@ -398,12 +315,12 @@ static void _do_register(void)
p->poll = _do_poll; p->poll = _do_poll;
p->fork = _do_fork; p->fork = _do_fork;
p->is_set = __fd_is_set; p->is_set = NULL;
p->set = __fd_set; p->set = NULL;
p->wai = __fd_set; p->wai = NULL;
p->clr = __fd_clr; p->clr = NULL;
p->rem = __fd_rem; p->rem = NULL;
p->clo = __fd_clo; p->clo = NULL;
} }

View File

@ -1,332 +0,0 @@
/*
* FD polling functions for Speculative I/O combined with Linux epoll()
*
* Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <common/compat.h>
#include <common/config.h>
#include <common/debug.h>
#include <common/epoll.h>
#include <common/standard.h>
#include <common/ticks.h>
#include <common/time.h>
#include <common/tools.h>
#include <types/global.h>
#include <proto/fd.h>
#include <proto/signal.h>
#include <proto/task.h>
static int absmaxevents = 0; // absolute maximum amounts of polled events
/* private data */
static struct epoll_event *epoll_events;
static int epoll_fd;
/* This structure may be used for any purpose. Warning! do not use it in
* recursive functions !
*/
static struct epoll_event ev;
/*
* speculative epoll() poller
*/
REGPRM2 static void _do_poll(struct poller *p, int exp)
{
int status, eo, en;
int fd, opcode;
int count;
int updt_idx;
int wait_time;
/* first, scan the update list to find changes */
for (updt_idx = 0; updt_idx < fd_nbupdt; updt_idx++) {
fd = fd_updt[updt_idx];
en = fdtab[fd].spec_e & 15; /* new events */
eo = fdtab[fd].spec_e >> 4; /* previous events */
if (fdtab[fd].owner && (eo ^ en)) {
if ((eo ^ en) & FD_EV_POLLED_RW) {
/* poll status changed */
if ((en & FD_EV_POLLED_RW) == 0) {
/* fd removed from poll list */
opcode = EPOLL_CTL_DEL;
}
else if ((eo & FD_EV_POLLED_RW) == 0) {
/* new fd in the poll list */
opcode = EPOLL_CTL_ADD;
}
else {
/* fd status changed */
opcode = EPOLL_CTL_MOD;
}
/* construct the epoll events based on new state */
ev.events = 0;
if (en & FD_EV_POLLED_R)
ev.events |= EPOLLIN;
if (en & FD_EV_POLLED_W)
ev.events |= EPOLLOUT;
ev.data.fd = fd;
epoll_ctl(epoll_fd, opcode, fd, &ev);
}
fdtab[fd].spec_e = (en << 4) + en; /* save new events */
if (!(en & FD_EV_ACTIVE_RW)) {
/* This fd doesn't use any active entry anymore, we can
* kill its entry.
*/
release_spec_entry(fd);
}
else if ((en & ~eo) & FD_EV_ACTIVE_RW) {
/* we need a new spec entry now */
alloc_spec_entry(fd);
}
}
fdtab[fd].updated = 0;
fdtab[fd].new = 0;
}
fd_nbupdt = 0;
/* compute the epoll_wait() timeout */
if (fd_nbspec || run_queue || signal_queue_len) {
/* Maybe we still have events in the spec list, or there are
* some tasks left pending in the run_queue, so we must not
* wait in epoll() otherwise we would delay their delivery by
* the next timeout.
*/
wait_time = 0;
}
else {
if (!exp)
wait_time = MAX_DELAY_MS;
else if (tick_is_expired(exp, now_ms))
wait_time = 0;
else {
wait_time = TICKS_TO_MS(tick_remain(now_ms, exp)) + 1;
if (wait_time > MAX_DELAY_MS)
wait_time = MAX_DELAY_MS;
}
}
/* now let's wait for polled events */
fd = MIN(maxfd, global.tune.maxpollevents);
gettimeofday(&before_poll, NULL);
status = epoll_wait(epoll_fd, epoll_events, fd, wait_time);
tv_update_date(wait_time, status);
measure_idle();
/* process polled events */
for (count = 0; count < status; count++) {
int e = epoll_events[count].events;
fd = epoll_events[count].data.fd;
if (!fdtab[fd].owner)
continue;
/* it looks complicated but gcc can optimize it away when constants
* have same values.
*/
fdtab[fd].ev &= FD_POLL_STICKY;
fdtab[fd].ev |=
((e & EPOLLIN ) ? FD_POLL_IN : 0) |
((e & EPOLLPRI) ? FD_POLL_PRI : 0) |
((e & EPOLLOUT) ? FD_POLL_OUT : 0) |
((e & EPOLLERR) ? FD_POLL_ERR : 0) |
((e & EPOLLHUP) ? FD_POLL_HUP : 0);
if (fdtab[fd].iocb && fdtab[fd].owner && fdtab[fd].ev) {
int new_updt, old_updt = fd_nbupdt; /* Save number of updates to detect creation of new FDs. */
/* Mark the events as speculative before processing
* them so that if nothing can be done we don't need
* to poll again.
*/
if (fdtab[fd].ev & (FD_POLL_IN|FD_POLL_HUP|FD_POLL_ERR))
fd_ev_set(fd, DIR_RD);
if (fdtab[fd].ev & (FD_POLL_OUT|FD_POLL_ERR))
fd_ev_set(fd, DIR_WR);
fdtab[fd].iocb(fd);
/* One or more fd might have been created during the iocb().
* This mainly happens with new incoming connections that have
* just been accepted, so we'd like to process them immediately
* for better efficiency. Second benefit, if at the end the fds
* are disabled again, we can safely destroy their update entry
* to reduce the scope of later scans. This is the reason we
* scan the new entries backwards.
*/
for (new_updt = fd_nbupdt; new_updt > old_updt; new_updt--) {
fd = fd_updt[new_updt - 1];
if (!fdtab[fd].new)
continue;
fdtab[fd].new = 0;
fdtab[fd].ev &= FD_POLL_STICKY;
if ((fdtab[fd].spec_e & FD_EV_STATUS_R) == FD_EV_ACTIVE_R)
fdtab[fd].ev |= FD_POLL_IN;
if ((fdtab[fd].spec_e & FD_EV_STATUS_W) == FD_EV_ACTIVE_W)
fdtab[fd].ev |= FD_POLL_OUT;
if (fdtab[fd].ev && fdtab[fd].iocb && fdtab[fd].owner)
fdtab[fd].iocb(fd);
/* we can remove this update entry if it's the last one and is
* unused, otherwise we don't touch anything.
*/
if (new_updt == fd_nbupdt && fdtab[fd].spec_e == 0) {
fdtab[fd].updated = 0;
fd_nbupdt--;
}
}
}
}
/* the caller will take care of speculative events */
}
/*
* Initialization of the speculative epoll() poller.
* Returns 0 in case of failure, non-zero in case of success. If it fails, it
* disables the poller by setting its pref to 0.
*/
REGPRM1 static int _do_init(struct poller *p)
{
p->private = NULL;
epoll_fd = epoll_create(global.maxsock + 1);
if (epoll_fd < 0)
goto fail_fd;
/* See comments at the top of the file about this formula. */
absmaxevents = MAX(global.tune.maxpollevents, global.maxsock);
epoll_events = (struct epoll_event*)
calloc(1, sizeof(struct epoll_event) * absmaxevents);
if (epoll_events == NULL)
goto fail_ee;
return 1;
fail_ee:
close(epoll_fd);
epoll_fd = -1;
fail_fd:
p->pref = 0;
return 0;
}
/*
* Termination of the speculative epoll() poller.
* Memory is released and the poller is marked as unselectable.
*/
REGPRM1 static void _do_term(struct poller *p)
{
free(epoll_events);
if (epoll_fd >= 0) {
close(epoll_fd);
epoll_fd = -1;
}
epoll_events = NULL;
p->private = NULL;
p->pref = 0;
}
/*
* Check that the poller works.
* Returns 1 if OK, otherwise 0.
*/
REGPRM1 static int _do_test(struct poller *p)
{
int fd;
fd = epoll_create(global.maxsock + 1);
if (fd < 0)
return 0;
close(fd);
return 1;
}
/*
* Recreate the epoll file descriptor after a fork(). Returns 1 if OK,
* otherwise 0. It will ensure that all processes will not share their
* epoll_fd. Some side effects were encountered because of this, such
* as epoll_wait() returning an FD which was previously deleted.
*/
REGPRM1 static int _do_fork(struct poller *p)
{
if (epoll_fd >= 0)
close(epoll_fd);
epoll_fd = epoll_create(global.maxsock + 1);
if (epoll_fd < 0)
return 0;
return 1;
}
/*
* It is a constructor, which means that it will automatically be called before
* main(). This is GCC-specific but it works at least since 2.95.
* Special care must be taken so that it does not need any uninitialized data.
*/
__attribute__((constructor))
static void _do_register(void)
{
struct poller *p;
if (nbpollers >= MAX_POLLERS)
return;
epoll_fd = -1;
p = &pollers[nbpollers++];
p->name = "sepoll";
p->pref = 400;
p->private = NULL;
p->test = _do_test;
p->init = _do_init;
p->term = _do_term;
p->poll = _do_poll;
p->fork = _do_fork;
p->is_set = NULL;
p->set = NULL;
p->wai = NULL;
p->clr = NULL;
p->rem = NULL;
p->clo = NULL;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -308,9 +308,6 @@ void usage(char *name)
#if defined(ENABLE_EPOLL) #if defined(ENABLE_EPOLL)
" -de disables epoll() usage even when available\n" " -de disables epoll() usage even when available\n"
#endif #endif
#if defined(ENABLE_SEPOLL)
" -ds disables speculative epoll() usage even when available\n"
#endif
#if defined(ENABLE_KQUEUE) #if defined(ENABLE_KQUEUE)
" -dk disables kqueue() usage even when available\n" " -dk disables kqueue() usage even when available\n"
#endif #endif
@ -476,9 +473,6 @@ void init(int argc, char **argv)
#if defined(ENABLE_EPOLL) #if defined(ENABLE_EPOLL)
global.tune.options |= GTUNE_USE_EPOLL; global.tune.options |= GTUNE_USE_EPOLL;
#endif #endif
#if defined(ENABLE_SEPOLL)
global.tune.options |= GTUNE_USE_SEPOLL;
#endif
#if defined(ENABLE_KQUEUE) #if defined(ENABLE_KQUEUE)
global.tune.options |= GTUNE_USE_KQUEUE; global.tune.options |= GTUNE_USE_KQUEUE;
#endif #endif
@ -512,10 +506,6 @@ void init(int argc, char **argv)
else if (*flag == 'd' && flag[1] == 'e') else if (*flag == 'd' && flag[1] == 'e')
global.tune.options &= ~GTUNE_USE_EPOLL; global.tune.options &= ~GTUNE_USE_EPOLL;
#endif #endif
#if defined(ENABLE_SEPOLL)
else if (*flag == 'd' && flag[1] == 's')
global.tune.options &= ~GTUNE_USE_SEPOLL;
#endif
#if defined(ENABLE_POLL) #if defined(ENABLE_POLL)
else if (*flag == 'd' && flag[1] == 'p') else if (*flag == 'd' && flag[1] == 'p')
global.tune.options &= ~GTUNE_USE_POLL; global.tune.options &= ~GTUNE_USE_POLL;
@ -792,9 +782,6 @@ void init(int argc, char **argv)
if (!(global.tune.options & GTUNE_USE_EPOLL)) if (!(global.tune.options & GTUNE_USE_EPOLL))
disable_poller("epoll"); disable_poller("epoll");
if (!(global.tune.options & GTUNE_USE_SEPOLL))
disable_poller("sepoll");
if (!(global.tune.options & GTUNE_USE_POLL)) if (!(global.tune.options & GTUNE_USE_POLL))
disable_poller("poll"); disable_poller("poll");