os: Add epoll-like pollset implementation for AIX

AIX's poll only allows FD_SETSIZE entries in the fd list, which is
insufficient for expanded MaxClients.

As a bonus, x11perf -noop with ~250 xlogos connected is slightly faster
with pollset:

 before          after         Operation
---------   ----------------   --------------------
5750000.0   5990000.0 (1.04)   X protocol NoOperation

Signed-off-by: Peter Harris <pharris@opentext.com>
Acked-by: Keith Packard <keithp@keithp.com>
This commit is contained in:
Peter Harris 2017-10-12 16:48:36 -04:00 committed by Adam Jackson
parent bed2830099
commit 83c04ee6ea
2 changed files with 146 additions and 1 deletions

View File

@ -123,6 +123,7 @@ conf_data.set('HAVE_GETPROGNAME', cc.has_function('getprogname'))
conf_data.set('HAVE_GETZONEID', cc.has_function('getzoneid'))
conf_data.set('HAVE_MMAP', cc.has_function('mmap'))
conf_data.set('HAVE_POLL', cc.has_function('poll'))
conf_data.set('HAVE_POLLSET_CREATE', cc.has_function('pollset_create'))
conf_data.set('HAVE_POSIX_FALLOCATE', cc.has_function('posix_fallocate'))
conf_data.set('HAVE_REALLOCARRAY', cc.has_function('reallocarray', dependencies: libbsd_dep))
conf_data.set('HAVE_SETEUID', cc.has_function('seteuid'))

View File

@ -32,6 +32,12 @@
#include "ospoll.h"
#include "list.h"
#if !HAVE_OSPOLL && defined(HAVE_POLLSET_CREATE)
#include <sys/pollset.h>
#define POLLSET 1
#define HAVE_OSPOLL 1
#endif
#if !HAVE_OSPOLL && defined(HAVE_EPOLL_CREATE1)
#include <sys/epoll.h>
#define EPOLL 1
@ -44,6 +50,27 @@
#define HAVE_OSPOLL 1
#endif
#if POLLSET
// pollset-based implementation (as seen on AIX)
struct ospollfd {
int fd;
int xevents;
short revents;
enum ospoll_trigger trigger;
void (*callback)(int fd, int xevents, void *data);
void *data;
};
struct ospoll {
pollset_t ps;
struct ospollfd *fds;
int num;
int size;
};
#endif
#if EPOLL
#include <sys/epoll.h>
@ -104,7 +131,7 @@ ospoll_find(struct ospoll *ospoll, int fd)
#if EPOLL
int t = ospoll->fds[m]->fd;
#endif
#if POLL
#if POLL || POLLSET
int t = ospoll->fds[m].fd;
#endif
@ -168,6 +195,16 @@ array_delete(void *base, size_t num, size_t size, size_t pos)
struct ospoll *
ospoll_create(void)
{
#if POLLSET
struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
ospoll->ps = pollset_create(-1);
if (ospoll->ps < 0) {
free (ospoll);
return NULL;
}
return ospoll;
#endif
#if EPOLL
struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
@ -187,6 +224,14 @@ ospoll_create(void)
void
ospoll_destroy(struct ospoll *ospoll)
{
#if POLLSET
if (ospoll) {
assert (ospoll->num == 0);
pollset_destroy(ospoll->ps);
free(ospoll->fds);
free(ospoll);
}
#endif
#if EPOLL
if (ospoll) {
assert (ospoll->num == 0);
@ -213,6 +258,30 @@ ospoll_add(struct ospoll *ospoll, int fd,
void *data)
{
int pos = ospoll_find(ospoll, fd);
#if POLLSET
if (pos < 0) {
if (ospoll->num == ospoll->size) {
struct ospollfd *new_fds;
int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
if (!new_fds)
return FALSE;
ospoll->fds = new_fds;
ospoll->size = new_size;
}
pos = -pos - 1;
array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
ospoll->num++;
ospoll->fds[pos].fd = fd;
ospoll->fds[pos].xevents = 0;
ospoll->fds[pos].revents = 0;
}
ospoll->fds[pos].trigger = trigger;
ospoll->fds[pos].callback = callback;
ospoll->fds[pos].data = data;
#endif
#if EPOLL
struct ospollfd *osfd;
@ -301,6 +370,14 @@ ospoll_remove(struct ospoll *ospoll, int fd)
pos = ospoll_find(ospoll, fd);
if (pos >= 0) {
#if POLLSET
struct ospollfd *osfd = &ospoll->fds[pos];
struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
pollset_ctl(ospoll->ps, &ctl, 1);
array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
ospoll->num--;
#endif
#if EPOLL
struct ospollfd *osfd = ospoll->fds[pos];
struct epoll_event ev;
@ -346,6 +423,19 @@ ospoll_listen(struct ospoll *ospoll, int fd, int xevents)
int pos = ospoll_find(ospoll, fd);
if (pos >= 0) {
#if POLLSET
struct poll_ctl ctl = { .cmd = PS_MOD, .fd = fd };
if (xevents & X_NOTIFY_READ) {
ctl.events |= POLLIN;
ospoll->fds[pos].revents &= ~POLLIN;
}
if (xevents & X_NOTIFY_WRITE) {
ctl.events |= POLLOUT;
ospoll->fds[pos].revents &= ~POLLOUT;
}
pollset_ctl(ospoll->ps, &ctl, 1);
ospoll->fds[pos].xevents |= xevents;
#endif
#if EPOLL
struct ospollfd *osfd = ospoll->fds[pos];
osfd->xevents |= xevents;
@ -370,6 +460,22 @@ ospoll_mute(struct ospoll *ospoll, int fd, int xevents)
int pos = ospoll_find(ospoll, fd);
if (pos >= 0) {
#if POLLSET
struct ospollfd *osfd = &ospoll->fds[pos];
osfd->xevents &= ~xevents;
struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
pollset_ctl(ospoll->ps, &ctl, 1);
if (osfd->xevents) {
ctl.cmd = PS_ADD;
if (osfd->xevents & X_NOTIFY_READ) {
ctl.events |= POLLIN;
}
if (osfd->xevents & X_NOTIFY_WRITE) {
ctl.events |= POLLOUT;
}
pollset_ctl(ospoll->ps, &ctl, 1);
}
#endif
#if EPOLL
struct ospollfd *osfd = ospoll->fds[pos];
osfd->xevents &= ~xevents;
@ -389,6 +495,33 @@ int
ospoll_wait(struct ospoll *ospoll, int timeout)
{
int nready;
#if POLLSET
#define MAX_EVENTS 256
struct pollfd events[MAX_EVENTS];
nready = pollset_poll(ospoll->ps, events, MAX_EVENTS, timeout);
for (int i = 0; i < nready; i++) {
struct pollfd *ev = &events[i];
int pos = ospoll_find(ospoll, ev->fd);
struct ospollfd *osfd = &ospoll->fds[pos];
short revents = ev->revents;
short oldevents = osfd->revents;
osfd->revents = (revents & (POLLIN|POLLOUT));
if (osfd->trigger == ospoll_trigger_edge)
revents &= ~oldevents;
if (revents) {
int xevents = 0;
if (revents & POLLIN)
xevents |= X_NOTIFY_READ;
if (revents & POLLOUT)
xevents |= X_NOTIFY_WRITE;
if (revents & (~(POLLIN|POLLOUT)))
xevents |= X_NOTIFY_ERROR;
osfd->callback(osfd->fd, xevents, osfd->data);
}
}
#endif
#if EPOLL
#define MAX_EVENTS 256
struct epoll_event events[MAX_EVENTS];
@ -451,6 +584,14 @@ ospoll_wait(struct ospoll *ospoll, int timeout)
void
ospoll_reset_events(struct ospoll *ospoll, int fd)
{
#if POLLSET
int pos = ospoll_find(ospoll, fd);
if (pos < 0)
return;
ospoll->fds[pos].revents = 0;
#endif
#if POLL
int pos = ospoll_find(ospoll, fd);
@ -468,6 +609,9 @@ ospoll_data(struct ospoll *ospoll, int fd)
if (pos < 0)
return NULL;
#if POLLSET
return ospoll->fds[pos].data;
#endif
#if EPOLL
return ospoll->fds[pos]->data;
#endif