diff --git a/Knot.files b/Knot.files index bffc2aa25d6474addc68342434d34105d181c91c..01457e00ce720c3aadff295b06e7f88253fb6c56 100644 --- a/Knot.files +++ b/Knot.files @@ -44,12 +44,6 @@ src/common/evsched.c src/common/evsched.h src/common/fdset.c src/common/fdset.h -src/common/fdset_epoll.c -src/common/fdset_epoll.h -src/common/fdset_kqueue.c -src/common/fdset_kqueue.h -src/common/fdset_poll.c -src/common/fdset_poll.h src/common/getline.c src/common/getline.h src/common/hattrie/ahtable.c diff --git a/configure.ac b/configure.ac index 9ae64978453ecdaa7d66c867afce058a795f6e06..419e27e816d6061f2bb7c2cbe972708aa91c60ce 100644 --- a/configure.ac +++ b/configure.ac @@ -302,7 +302,7 @@ AC_TYPE_SSIZE_T AC_DEFINE([DSFMT_MEXP], [521], [DSFMT parameters]) # Checks for library functions. -AC_CHECK_FUNCS([clock_gettime gettimeofday epoll_wait fgetln getline kqueue madvise poll posix_memalign pselect pthread_setaffinity_np regcomp select sendmmsg setgroups]) +AC_CHECK_FUNCS([clock_gettime gettimeofday fgetln getline madvise poll posix_memalign pselect pthread_setaffinity_np regcomp select sendmmsg setgroups]) # Check for cpu_set_t/cpuset_t compatibility AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[cpu_set_t set; CPU_ZERO(&set);]])], diff --git a/src/Makefile.am b/src/Makefile.am index 191e6cccc5ddd8a55f607f01d89205f2fb7cd803..46fe2928b141eb2f926c31fc9293e8475313f945 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -218,12 +218,6 @@ libknots_la_SOURCES = \ common/prng.c \ common/fdset.h \ common/fdset.c \ - common/fdset_poll.h \ - common/fdset_poll.c \ - common/fdset_kqueue.h \ - common/fdset_kqueue.c \ - common/fdset_epoll.h \ - common/fdset_epoll.c \ common/getline.h \ common/getline.c \ common/log.c \ diff --git a/src/common/fdset.c b/src/common/fdset.c index 3b4b75aa73ab770e23eb332e7747cd951fb58cdc..5f93d278ff31992b01c8546592c7a8bf2d23092b 100644 --- a/src/common/fdset.c +++ b/src/common/fdset.c @@ -15,12 +15,11 @@ */ #include <config.h> -#include <dlfcn.h> +#include <stdlib.h> #include <string.h> -#include <stdio.h> #include <time.h> -#include <stdlib.h> #include "common/fdset.h" +#include "common.h" /* Workarounds for clock_gettime() not available on some platforms. */ #ifdef HAVE_CLOCK_GETTIME @@ -34,171 +33,137 @@ typedef struct timeval timev_t; #error Neither clock_gettime() nor gettimeofday() found. At least one is required. #endif -struct fdset_backend_t _fdset_backend = { - NULL -}; +/* Realloc memory or return error (part of fdset_resize). */ +#define MEM_RESIZE(tmp, p, n) \ + if ((tmp = realloc((p), (n))) == NULL) \ + return KNOT_ENOMEM; \ + (p) = tmp; -/*! \brief Set backend implementation. */ -static void fdset_set_backend(struct fdset_backend_t *backend) { - memcpy(&_fdset_backend, backend, sizeof(struct fdset_backend_t)); +static int fdset_resize(fdset_t *set, unsigned size) +{ + void *tmp = NULL; + MEM_RESIZE(tmp, set->ctx, size * sizeof(void*)); + MEM_RESIZE(tmp, set->pfd, size * sizeof(struct pollfd)); + MEM_RESIZE(tmp, set->timeout, size * sizeof(timev_t)); + set->size = size; + return KNOT_EOK; } -/* Linux epoll API. */ -#ifdef HAVE_EPOLL_WAIT - #include "common/fdset_epoll.h" -#endif /* HAVE_EPOLL_WAIT */ - -/* BSD kqueue API */ -#ifdef HAVE_KQUEUE - #include "common/fdset_kqueue.h" -#endif /* HAVE_KQUEUE */ - -/* POSIX poll API */ -#ifdef HAVE_POLL - #include "common/fdset_poll.h" -#endif /* HAVE_POLL */ - -/*! \brief Bootstrap polling subsystem (it is called automatically). */ -void __attribute__ ((constructor)) fdset_init() +int fdset_init(fdset_t *set, unsigned size) { - /* Preference: epoll */ -#ifdef HAVE_EPOLL_WAIT - if (dlsym(RTLD_DEFAULT, "epoll_wait") != 0) { - fdset_set_backend(&FDSET_EPOLL); - return; + if (set == NULL) { + return KNOT_EINVAL; } -#endif - /* Preference: kqueue */ -#ifdef HAVE_KQUEUE - if (dlsym(RTLD_DEFAULT, "kqueue") != 0) { - fdset_set_backend(&FDSET_KQUEUE); - return; - } -#endif + memset(set, 0, sizeof(fdset_t)); + return fdset_resize(set, size); +} - /* Fallback: poll */ -#ifdef HAVE_POLL - if (dlsym(RTLD_DEFAULT, "poll") != 0) { - fdset_set_backend(&FDSET_POLL); - return; +int fdset_clear(fdset_t* set) +{ + if (set == NULL) { + return KNOT_EINVAL; } -#endif - /* This shouldn't happen. */ - fprintf(stderr, "fdset: fatal error - no valid fdset backend found\n"); - return; + free(set->ctx); + free(set->pfd); + free(set->timeout); + memset(set, 0, sizeof(fdset_t)); + return KNOT_EOK; } -/*! - * \brief Compare file descriptors. - * - * \param a File descriptor. - * \param b File descriptor. - * - * \retval -1 if a < b - * \retval 0 if a == b - * \retval 1 if a > b - */ -static inline int fdset_compare(void *a, void *b) +int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx) { - if (a > b) return 1; - if (a < b) return -1; - return 0; -} - -fdset_t *fdset_new() { - fdset_t* set = _fdset_backend.fdset_new(); - fdset_base_t *base = (fdset_base_t*)set; - if (base != NULL) { - /* Create atimes list. */ - base->atimes = skip_create_list(fdset_compare); - if (base->atimes == NULL) { - fdset_destroy(set); - set = NULL; - } + if (set == NULL || fd < 0) { + return KNOT_EINVAL; } - return set; + + /* Realloc needed. */ + if (set->n == set->size && fdset_resize(set, set->size + FDSET_INIT_SIZE)) + return KNOT_ENOMEM; + + /* Initialize. */ + int i = set->n++; + set->pfd[i].fd = fd; + set->pfd[i].events = events; + set->pfd[i].revents = 0; + set->ctx[i] = ctx; + set->timeout[i] = 0; + + /* Return index to this descriptor. */ + return i; } -int fdset_destroy(fdset_t* fdset) { - fdset_base_t *base = (fdset_base_t*)fdset; - if (base != NULL && base->atimes != NULL) { - skip_destroy_list(&base->atimes, NULL, free); +int fdset_remove(fdset_t *set, unsigned i) +{ + if (set == NULL || i >= set->n) { + return KNOT_EINVAL; } - return _fdset_backend.fdset_destroy(fdset); -} -int fdset_remove(fdset_t *fdset, int fd) { - fdset_base_t *base = (fdset_base_t*)fdset; - if (base != NULL && base->atimes != NULL) { - skip_remove(base->atimes, (void*)((size_t)fd), NULL, free); + /* Decrement number of elms. */ + --set->n; + + /* Nothing else if it is the last one. + * Move last -> i if some remain. */ + unsigned last = set->n; /* Already decremented */ + if (i < last) { + set->pfd[i] = set->pfd[last]; + set->timeout[i] = set->timeout[last]; + set->ctx[i] = set->ctx[last]; } - return _fdset_backend.fdset_remove(fdset, fd); + + return KNOT_EOK; } -int fdset_set_watchdog(fdset_t* fdset, int fd, int interval) +int fdset_set_watchdog(fdset_t* set, int i, int interval) { - fdset_base_t *base = (fdset_base_t*)fdset; - if (base == NULL || base->atimes == NULL) { - return -1; + if (set == NULL || i >= set->n) { + return KNOT_EINVAL; } /* Lift watchdog if interval is negative. */ if (interval < 0) { - skip_remove(base->atimes, (void*)((size_t)fd), NULL, free); - return 0; - } - - /* Find if exists. */ - timev_t *ts = NULL; - ts = (timev_t*)skip_find(base->atimes, (void*)((size_t)fd)); - if (ts == NULL) { - ts = malloc(sizeof(timev_t)); - if (ts == NULL) { - return -1; - } - skip_insert(base->atimes, (void*)((size_t)fd), (void*)ts, NULL); + set->timeout[i] = 0; + return KNOT_EOK; } /* Update clock. */ - if (time_now(ts) < 0) { - return -1; - } + timev_t now; + if (time_now(&now) < 0) + return KNOT_ERROR; - ts->tv_sec += interval; /* Only seconds precision. */ - return 0; + set->timeout[i] = now.tv_sec + interval; /* Only seconds precision. */ + return KNOT_EOK; } -int fdset_sweep(fdset_t* fdset, void(*cb)(fdset_t*, int, void*), void *data) +int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data) { - fdset_base_t *base = (fdset_base_t*)fdset; - if (base == NULL || base->atimes == NULL) { - return -1; + if (set == NULL || cb == NULL) { + return KNOT_EINVAL; } /* Get time threshold. */ timev_t now; if (time_now(&now) < 0) { - return -1; + return KNOT_ERROR; } - /* Inspect all nodes. */ - int sweeped = 0; - const skip_node_t *n = skip_first(base->atimes); - while (n != NULL) { - const skip_node_t* pnext = skip_next(n); - - /* Evaluate */ - timev_t *ts = (timev_t*)n->value; - if (ts->tv_sec <= now.tv_sec) { - cb(fdset, (int)(((ssize_t)n->key)), data); - ++sweeped; + unsigned i = 0; + while (i < set->n) { + + /* Check sweep state, remove if requested. */ + if (set->timeout[i] > 0 && set->timeout[i] <= now.tv_sec) { + if (cb(set, i, data) == FDSET_SWEEP) { + if (fdset_remove(set, i) == KNOT_EOK) + continue; /* Stay on the index. */ + } } - n = pnext; + + /* Next descriptor. */ + ++i; } - return sweeped; + return KNOT_EOK; } /* OpenBSD compatibility. */ diff --git a/src/common/fdset.h b/src/common/fdset.h index e1facdb06e2ef64fb1fdd3fe68d94ac8e6bbbd77..f0ef84928ea2742831e8b4b075093b1275ab1b56 100644 --- a/src/common/fdset.h +++ b/src/common/fdset.h @@ -18,12 +18,7 @@ * * \author Marek Vavrusa <marek.vavrusa@nic.cz> * - * \brief Wrapper for native I/O multiplexing. - * - * Selects best implementation according to config. - * - poll() - * - epoll() - * - kqueue() + * \brief I/O multiplexing with context and timeouts for each fd. * * \addtogroup common_lib * @{ @@ -32,88 +27,36 @@ #ifndef _KNOTD_FDSET_H_ #define _KNOTD_FDSET_H_ +#include <config.h> #include <stddef.h> -#ifdef HAVE_SYS_SELECT_H - #include <sys/select.h> -#endif -#ifdef HAVE_SYS_TIME_H - #include <sys/time.h> -#endif -#ifdef HAVE_SIGNAL_H - #include <signal.h> -#endif -#include "skip-list.h" -#include "mempattern.h" - -/*! \brief Waiting for completion constants. */ -enum fdset_wait_t { - OS_EV_FOREVER = -1, /*!< Wait forever. */ - OS_EV_NOWAIT = 0 /*!< Return immediately. */ +#include <poll.h> +#include <sys/time.h> +#include <signal.h> + +#define FDSET_INIT_SIZE 256 /* Resize step. */ + +/*! \brief Set of filedescriptors with associated context and timeouts. */ +typedef struct fdset { + unsigned n; /*!< Active fds. */ + unsigned size; /*!< Array size (allocated). */ + void* *ctx; /*!< Context for each fd. */ + struct pollfd *pfd; /*!< poll state for each fd */ + time_t *timeout; /*!< Timeout for each fd (seconds precision). */ +} fdset_t; + +/*! \brief Mark-and-sweep state. */ +enum fdset_sweep_state { + FDSET_KEEP, + FDSET_SWEEP }; -/*! \brief Base for implementation-specific fdsets. */ -typedef struct fdset_base_t { - skip_list_t *atimes; -} fdset_base_t; +/*! \brief Sweep callback (set, index, data) */ +typedef enum fdset_sweep_state (*fdset_sweep_cb_t)(fdset_t*, int, void*); /*! - * \brief Opaque pointer to implementation-specific fdset data. - * \warning Implementation MUST have fdset_base_t member on the first place. - * Example: - * struct fdset_t { - * fdset_base_t base; - * ...other members... - * } + * \brief Initialize fdset to given size. */ -typedef struct fdset_t fdset_t; - -/*! \brief Unified event types. */ -enum fdset_event_t { - OS_EV_READ = 1 << 0, /*!< Readable event. */ - OS_EV_WRITE = 1 << 1, /*!< Writeable event. */ - OS_EV_ERROR = 1 << 2 /*!< Error event. */ -}; - -/*! \brief File descriptor set iterator. */ -typedef struct fdset_it_t { - int fd; /*!< Current file descriptor. */ - int events; /*!< Returned events. */ - size_t pos; /* Internal usage. */ -} fdset_it_t; - -/*! - * \brief File descriptor set implementation backend. - * \note Functions documentation following. - * \internal - */ -struct fdset_backend_t -{ - fdset_t* (*fdset_new)(); - int (*fdset_destroy)(fdset_t*); - int (*fdset_add)(fdset_t*, int, int); - int (*fdset_remove)(fdset_t*, int); - int (*fdset_wait)(fdset_t*, int); - int (*fdset_begin)(fdset_t*, fdset_it_t*); - int (*fdset_end)(fdset_t*, fdset_it_t*); - int (*fdset_next)(fdset_t*, fdset_it_t*); - const char* (*fdset_method)(); -}; - -/*! - * \brief Selected backend. - * \internal - */ -extern struct fdset_backend_t _fdset_backend; - -/*! - * \brief Create new fdset. - * - * FDSET implementation depends on system. - * - * \retval Pointer to initialized FDSET structure if successful. - * \retval NULL on error. - */ -fdset_t *fdset_new(); +int fdset_init(fdset_t *set, unsigned size); /*! * \brief Destroy FDSET. @@ -121,125 +64,61 @@ fdset_t *fdset_new(); * \retval 0 if successful. * \retval -1 on error. */ -int fdset_destroy(fdset_t* fdset); +int fdset_clear(fdset_t* set); /*! * \brief Add file descriptor to watched set. * - * \param fdset Target set. + * \param set Target set. * \param fd Added file descriptor. * \param events Mask of watched events. + * \param ctx Context (optional). * - * \retval 0 if successful. + * \retval index of the added fd if successful. * \retval -1 on errors. */ -static inline int fdset_add(fdset_t *fdset, int fd, int events) { - return _fdset_backend.fdset_add(fdset, fd, events); -} - +int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx); /*! * \brief Remove file descriptor from watched set. * - * \param fdset Target set. - * \param fd File descriptor to be removed. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_remove(fdset_t *fdset, int fd); - -/*! - * \brief Poll set for new events. - * - * \param fdset Target set. - * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds). - * - * \retval Number of events if successful. - * \retval -1 on errors. - */ -static inline int fdset_wait(fdset_t *fdset, int timeout) { - return _fdset_backend.fdset_wait(fdset, timeout); -} - -/*! - * \brief Set event iterator to the beginning of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -static inline int fdset_begin(fdset_t *fdset, fdset_it_t *it) { - return _fdset_backend.fdset_begin(fdset, it); -} - -/*! - * \brief Set event iterator to the end of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. + * \param set Target set. + * \param i Index of the removed fd. * * \retval 0 if successful. * \retval -1 on errors. */ -static inline int fdset_end(fdset_t *fdset, fdset_it_t *it) { - return _fdset_backend.fdset_end(fdset, it); -} - -/*! - * \brief Set event iterator to the next event. - * - * Event iterator fd will be set to -1 if next event doesn't exist. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -static inline int fdset_next(fdset_t *fdset, fdset_it_t *it) { - return _fdset_backend.fdset_next(fdset, it); -} - -/*! - * \brief Returned name of underlying poll method. - * - * \retval Name if successful. - * \retval NULL if no method was loaded (shouldn't happen). - */ -static inline const char* fdset_method() { - return _fdset_backend.fdset_method(); -} +int fdset_remove(fdset_t *set, unsigned i); /*! * \brief Set file descriptor watchdog interval. * - * Descriptors without activity in given interval - * can be disposed with fdset_sweep(). + * Set time (interval from now) after which the associated file descriptor + * should be sweeped (see fdset_sweep). Good example is setting a grace period + * of N seconds between socket activity. If socket is not active within + * <now, now + interval>, it is sweeped and potentially closed. * - * \param fdset Target set. - * \param fd File descriptor. + * \param set Target set. + * \param i Index for the file descriptor. * \param interval Allowed interval without activity (seconds). - * <0 removes watchdog interval. + * -1 disables watchdog timer * * \retval 0 if successful. * \retval -1 on errors. */ -int fdset_set_watchdog(fdset_t* fdset, int fd, int interval); +int fdset_set_watchdog(fdset_t* set, int i, int interval); /*! * \brief Sweep file descriptors with exceeding inactivity period. * * \param fdset Target set. * \param cb Callback for sweeped descriptors. - * \param data Custom data for sweep operation. + * \param data Pointer to extra data. * * \retval number of sweeped descriptors. * \retval -1 on errors. */ -int fdset_sweep(fdset_t* fdset, void(*cb)(fdset_t*, int, void*), void *data); +int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data); /*! * \brief pselect(2) compatibility wrapper. diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c deleted file mode 100644 index f889dd33c557430fb884966e891f56125ecb086f..0000000000000000000000000000000000000000 --- a/src/common/fdset_epoll.c +++ /dev/null @@ -1,205 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - 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 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <config.h> - -#ifdef HAVE_EPOLL_WAIT - -#include <sys/epoll.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> - -#include "fdset_epoll.h" -#include "skip-list.h" - -#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */ - -struct fdset_t { - fdset_base_t _base; - int epfd; - struct epoll_event *events; - size_t nfds; - size_t reserved; - size_t polled; -}; - -fdset_t *fdset_epoll_new() -{ - fdset_t *set = malloc(sizeof(fdset_t)); - if (set) { - /* Blank memory. */ - memset(set, 0, sizeof(fdset_t)); - - /* Create epoll fd. */ - set->epfd = epoll_create(OS_FDS_CHUNKSIZE); - } - - return set; -} - -int fdset_epoll_destroy(fdset_t * fdset) -{ - if(fdset == NULL) { - return -1; - } - - /* Teardown epoll. */ - close(fdset->epfd); - - /* OK if NULL. */ - free(fdset->events); - free(fdset); - return 0; -} - -int fdset_epoll_add(fdset_t *fdset, int fd, int events) -{ - if (fdset == NULL || fd < 0 || events <= 0) { - return -1; - } - - /* Realloc needed. */ - if (mreserve((char **)&fdset->events, sizeof(struct epoll_event), - fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved) < 0) { - return -1; - } - - /* Add to epoll set. */ - struct epoll_event ev; - memset(&ev, 0, sizeof(struct epoll_event)); - ev.events = EPOLLIN; - ev.data.fd = fd; - if (epoll_ctl(fdset->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) { - return -1; - } - - ++fdset->nfds; - return 0; -} - -int fdset_epoll_remove(fdset_t *fdset, int fd) -{ - if (fdset == NULL || fd < 0) { - return -1; - } - - /* Attempt to remove from set. */ - struct epoll_event ev; - memset(&ev, 0, sizeof(struct epoll_event)); - if (epoll_ctl(fdset->epfd, EPOLL_CTL_DEL, fd, &ev) < 0) { - return -1; - } - - /* Overwrite current item. */ - --fdset->nfds; - - /* Trim excessive memory if possible (retval is not interesting). */ - mreserve((char **)&fdset->events, sizeof(struct epoll_event), - fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved); - - return 0; -} - -int fdset_epoll_wait(fdset_t *fdset, int timeout) -{ - if (fdset == NULL || fdset->nfds < 1 || fdset->events == NULL) { - return -1; - } - - /* Poll new events. */ - fdset->polled = 0; - int nfds = epoll_wait(fdset->epfd, fdset->events, fdset->nfds, timeout); - - /* Check. */ - if (nfds < 0) { - return -1; - } - - /* Events array is ordered from 0 to nfds. */ - fdset->polled = nfds; - return nfds; -} - -int fdset_epoll_begin(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL) { - return -1; - } - - /* Find first. */ - it->pos = 0; - return fdset_next(fdset, it); -} - -int fdset_epoll_end(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL || fdset->nfds < 1) { - return -1; - } - - /* Check for polled events. */ - if (fdset->polled < 1) { - it->fd = -1; - it->pos = 0; - return -1; - } - - /* No end found, ends on the beginning. */ - size_t nid = fdset->polled - 1; - it->fd = fdset->events[nid].data.fd; - it->pos = nid; - it->events = 0; - return -1; -} - -int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL || fdset->nfds < 1) { - return -1; - } - - /* Check boundaries. */ - if (it->pos >= fdset->polled || it->pos >= fdset->nfds) { - return -1; - } - - /* Select next. */ - size_t nid = it->pos++; - it->fd = fdset->events[nid].data.fd; - it->events = 0; - return 0; -} - -const char* fdset_epoll_method() -{ - return "epoll"; -} - -/* Package APIs. */ -struct fdset_backend_t FDSET_EPOLL = { - .fdset_new = fdset_epoll_new, - .fdset_destroy = fdset_epoll_destroy, - .fdset_add = fdset_epoll_add, - .fdset_remove = fdset_epoll_remove, - .fdset_wait = fdset_epoll_wait, - .fdset_begin = fdset_epoll_begin, - .fdset_end = fdset_epoll_end, - .fdset_next = fdset_epoll_next, - .fdset_method = fdset_epoll_method -}; - -#endif diff --git a/src/common/fdset_epoll.h b/src/common/fdset_epoll.h deleted file mode 100644 index 58f25f81e7df57c51eadb6dbc52bb7a15b1c070a..0000000000000000000000000000000000000000 --- a/src/common/fdset_epoll.h +++ /dev/null @@ -1,132 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - 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 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -/*! - * \file fdset_epoll.h - * - * \author Marek Vavrusa <marek.vavrusa@nic.cz> - * - * \brief Wrapper for epoll I/O multiplexing. - * - * \addtogroup common_lib - * @{ - */ - -#ifndef _KNOTD_FDSET_EPOLL_H_ -#define _KNOTD_FDSET_EPOLL_H_ - -#include "fdset.h" - -/*! - * \brief Create new fdset. - * - * Linux epoll() backend. - * - * \retval Pointer to initialized FDSET structure if successful. - * \retval NULL on error. - */ -fdset_t *fdset_epoll_new(); - -/*! - * \brief Destroy FDSET. - * - * \retval 0 if successful. - * \retval -1 on error. - */ -int fdset_epoll_destroy(fdset_t * fdset); - -/*! - * \brief Add file descriptor to watched set. - * - * \param fdset Target set. - * \param fd Added file descriptor. - * \param events Mask of watched events. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_epoll_add(fdset_t *fdset, int fd, int events); - -/*! - * \brief Remove file descriptor from watched set. - * - * \param fdset Target set. - * \param fd File descriptor to be removed. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_epoll_remove(fdset_t *fdset, int fd); - -/*! - * \brief Poll set for new events. - * - * \param fdset Target set. - * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds). - * - * \retval Number of events if successful. - * \retval -1 on errors. - */ -int fdset_epoll_wait(fdset_t *fdset, int timeout); - -/*! - * \brief Set event iterator to the beginning of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_epoll_begin(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Set event iterator to the end of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_epoll_end(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Set event iterator to the next event. - * - * Event iterator fd will be set to -1 if next event doesn't exist. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Returned name of epoll method. - * - * \retval Name if successful. - * \retval NULL if no method was loaded (shouldn't happen). - */ -const char* fdset_epoll_method(); - -/*! \brief Exported API. */ -extern struct fdset_backend_t FDSET_EPOLL; - -#endif /* _KNOTD_FDSET_EPOLL_H_ */ - -/*! @} */ diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c deleted file mode 100644 index 7c52f71ba96cac4c82ccf5f7104573d2f96b5597..0000000000000000000000000000000000000000 --- a/src/common/fdset_kqueue.c +++ /dev/null @@ -1,260 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - 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 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <config.h> -#ifdef HAVE_KQUEUE - -#include <stdint.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/event.h> -#include <sys/time.h> -#include <time.h> - -#include "fdset_kqueue.h" - -#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */ -#define OS_FDS_KEEPCHUNKS 32 /*!< Will attempt to free memory when reached. */ - -struct fdset_t { - fdset_base_t _base; - int kq; - struct kevent *events; - struct kevent *revents; - size_t nfds; - size_t reserved; - size_t rreserved; - size_t polled; -}; - -fdset_t *fdset_kqueue_new() -{ - fdset_t *set = malloc(sizeof(fdset_t)); - if (set) { - /* Blank memory. */ - memset(set, 0, sizeof(fdset_t)); - - /* Create kqueue fd. */ - set->kq = kqueue(); - if (set->kq < 0) { - free(set); - set = 0; - } - } - - return set; -} - -int fdset_kqueue_destroy(fdset_t * fdset) -{ - if(fdset == NULL) { - return -1; - } - - /* Teardown kqueue. */ - close(fdset->kq); - - /* OK if NULL. */ - free(fdset->revents); - free(fdset->events); - free(fdset); - return 0; -} - -int fdset_kqueue_add(fdset_t *fdset, int fd, int events) -{ - if (fdset == NULL || fd < 0 || events <= 0) { - return -1; - } - - /* Realloc needed. */ - int ret = 0; - ret += mreserve((char **)&fdset->events, sizeof(struct kevent), - fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved); - ret += mreserve((char **)&fdset->revents, sizeof(struct kevent), - fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->rreserved); - if (ret != 0) { - return ret; - } - - /* Add to kqueue set. */ - int evfilt = EVFILT_READ; - EV_SET(&fdset->events[fdset->nfds], fd, evfilt, - EV_ADD|EV_ENABLE, 0, 0, 0); - memset(fdset->revents + fdset->nfds, 0, sizeof(struct kevent)); - - ++fdset->nfds; - return 0; -} - -int fdset_kqueue_remove(fdset_t *fdset, int fd) -{ - if (fdset == NULL || fd < 0) { - return -1; - } - - /* Find in set. */ - int pos = -1; - for (int i = 0; i < fdset->nfds; ++i) { - if (fdset->events[i].ident == fd) { - pos = i; - break; - } - } - - if (pos < 0) { - return -1; - } - - /* Remove filters. */ - EV_SET(&fdset->events[pos], fd, EVFILT_READ, - EV_DISABLE|EV_DELETE, 0, 0, 0); - - /* Attempt to remove from set. */ - size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct kevent); - memmove(fdset->events + pos, fdset->events + (pos + 1), remaining); - - /* Attempt to remove from revents set. */ - pos = -1; - for (int i = 0; i < fdset->nfds; ++i) { - if (fdset->events[i].ident == fd) { - pos = i; - break; - } - } - if (pos >= 0) { - size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct kevent); - memmove(fdset->revents + pos, fdset->revents + (pos + 1), remaining); - } - - - /* Overwrite current item. */ - --fdset->nfds; - - /* Trim excessive memory if possible (retval is not interesting). */ - mreserve((char **)&fdset->events, sizeof(struct kevent), - fdset->nfds, OS_FDS_CHUNKSIZE, &fdset->reserved); - mreserve((char **)&fdset->revents, sizeof(struct kevent), - fdset->nfds, OS_FDS_CHUNKSIZE, &fdset->rreserved); - - return 0; -} - -int fdset_kqueue_wait(fdset_t *fdset, int timeout) -{ - if (fdset == NULL || fdset->nfds < 1 || fdset->events == NULL) { - return -1; - } - - /* Set timeout. */ - struct timespec tmval; - struct timespec *tm = NULL; - if (timeout == 0) { - tmval.tv_sec = tmval.tv_nsec = 0; - tm = &tmval; - } else if (timeout > 0) { - tmval.tv_sec = timeout / 1000; /* ms -> s */ - timeout -= tmval.tv_sec * 1000; /* Cut off */ - tmval.tv_nsec = timeout * 1000000L; /* ms -> ns */ - tm = &tmval; - } - - /* Poll new events. */ - fdset->polled = 0; - int nfds = kevent(fdset->kq, fdset->events, fdset->nfds, - fdset->revents, fdset->nfds, tm); - - /* Check. */ - if (nfds < 0) { - return -1; - } - - /* Events array is ordered from 0 to nfds. */ - fdset->polled = nfds; - return nfds; -} - -int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL) { - return -1; - } - - /* Find first. */ - it->pos = 0; - it->fd = -1; - return fdset_next(fdset, it); -} - -int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL || fdset->nfds < 1) { - return -1; - } - - /* Check for polled events. */ - if (fdset->polled < 1) { - it->fd = -1; - it->pos = 0; - return -1; - } - - /* No end found, ends on the beginning. */ - size_t nid = fdset->polled - 1; - it->fd = fdset->revents[nid].ident; - it->pos = nid; - it->events = 0; /*! \todo Map events. */ - return -1; -} - -int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL || fdset->polled < 1) { - return -1; - } - - /* Check boundaries. */ - if (it->pos >= fdset->polled || it->pos >= fdset->nfds) { - return -1; - } - - /* Select next. */ - size_t nid = it->pos++; - it->fd = fdset->revents[nid].ident; - it->events = 0; /*! \todo Map events. */ - return 0; -} - -const char* fdset_kqueue_method() -{ - return "kqueue"; -} - -/* Package APIs. */ -struct fdset_backend_t FDSET_KQUEUE = { - .fdset_new = fdset_kqueue_new, - .fdset_destroy = fdset_kqueue_destroy, - .fdset_add = fdset_kqueue_add, - .fdset_remove = fdset_kqueue_remove, - .fdset_wait = fdset_kqueue_wait, - .fdset_begin = fdset_kqueue_begin, - .fdset_end = fdset_kqueue_end, - .fdset_next = fdset_kqueue_next, - .fdset_method = fdset_kqueue_method -}; - -#endif diff --git a/src/common/fdset_kqueue.h b/src/common/fdset_kqueue.h deleted file mode 100644 index 4b650a7b90a26389c162e200038884c5bc3d0d0c..0000000000000000000000000000000000000000 --- a/src/common/fdset_kqueue.h +++ /dev/null @@ -1,132 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - 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 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -/*! - * \file fdset_kqueue.h - * - * \author Marek Vavrusa <marek.vavrusa@nic.cz> - * - * \brief Wrapper for kqueue I/O multiplexing. - * - * \addtogroup common_lib - * @{ - */ - -#ifndef _KNOTD_FDSET_KQUEUE_H_ -#define _KNOTD_FDSET_KQUEUE_H_ - -#include "fdset.h" - -/*! - * \brief Create new fdset. - * - * BSD kqueue() backend. - * - * \retval Pointer to initialized FDSET structure if successful. - * \retval NULL on error. - */ -fdset_t *fdset_kqueue_new(); - -/*! - * \brief Destroy FDSET. - * - * \retval 0 if successful. - * \retval -1 on error. - */ -int fdset_kqueue_destroy(fdset_t * fdset); - -/*! - * \brief Add file descriptor to watched set. - * - * \param fdset Target set. - * \param fd Added file descriptor. - * \param events Mask of watched events. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_kqueue_add(fdset_t *fdset, int fd, int events); - -/*! - * \brief Remove file descriptor from watched set. - * - * \param fdset Target set. - * \param fd File descriptor to be removed. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_kqueue_remove(fdset_t *fdset, int fd); - -/*! - * \brief Poll set for new events. - * - * \param fdset Target set. - * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds). - * - * \retval Number of events if successful. - * \retval -1 on errors. - */ -int fdset_kqueue_wait(fdset_t *fdset, int timeout); - -/*! - * \brief Set event iterator to the beginning of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_kqueue_begin(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Set event iterator to the end of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_kqueue_end(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Set event iterator to the next event. - * - * Event iterator fd will be set to -1 if next event doesn't exist. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_kqueue_next(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Returned name of kqueue method. - * - * \retval Name if successful. - * \retval NULL if no method was loaded (shouldn't happen). - */ -const char* fdset_kqueue_method(); - -/*! \brief Exported API. */ -extern struct fdset_backend_t FDSET_KQUEUE; - -#endif /* _KNOTD_FDSET_KQUEUE_H_ */ - -/*! @} */ diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c deleted file mode 100644 index e16ae11e0bfd8ced9eef2b26bec7df8539324767..0000000000000000000000000000000000000000 --- a/src/common/fdset_poll.c +++ /dev/null @@ -1,218 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - 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 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <config.h> - -#ifdef HAVE_POLL - -#include <stdlib.h> -#include <string.h> -#include <sys/poll.h> -#include <stddef.h> - -#include "common/fdset_poll.h" - -#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */ - -struct fdset_t { - fdset_base_t _base; - struct pollfd *fds; - nfds_t nfds; - size_t reserved; - size_t polled; - size_t begin; -}; - -fdset_t *fdset_poll_new() -{ - fdset_t *set = malloc(sizeof(fdset_t)); - if (set != NULL) { - memset(set, 0, sizeof(fdset_t)); - } - - return set; -} - -int fdset_poll_destroy(fdset_t * fdset) -{ - if(fdset == NULL) { - return -1; - } - - /* OK if NULL. */ - free(fdset->fds); - free(fdset); - return 0; -} - -int fdset_poll_add(fdset_t *fdset, int fd, int events) -{ - if (fdset == NULL || fd < 0 || events <= 0) { - return -1; - } - - /* Realloc needed. */ - if (mreserve((char **)&fdset->fds, sizeof(struct pollfd), - fdset->nfds + 1, OS_FDS_CHUNKSIZE, &fdset->reserved) < 0) { - return -1; - } - - /* Append. */ - int nid = fdset->nfds++; - fdset->fds[nid].fd = fd; - fdset->fds[nid].events = POLLIN; - fdset->fds[nid].revents = 0; - return 0; -} - -int fdset_poll_remove(fdset_t *fdset, int fd) -{ - if (fdset == NULL || fd < 0) { - return -1; - } - - /* Find file descriptor. */ - unsigned found = 0; - size_t pos = 0; - for (size_t i = 0; i < fdset->nfds; ++i) { - if (fdset->fds[i].fd == fd) { - found = 1; - pos = i; - break; - } - } - - /* Check. */ - if (!found) { - return -1; - } - - /* Overwrite current item. */ - size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct pollfd); - memmove(fdset->fds + pos, fdset->fds + (pos + 1), remaining); - --fdset->nfds; - - /* Trim excessive memory if possible (retval is not interesting). */ - mreserve((char **)&fdset->fds, sizeof(struct pollfd), fdset->nfds, - OS_FDS_CHUNKSIZE, &fdset->reserved); - - return 0; -} - -int fdset_poll_wait(fdset_t *fdset, int timeout) -{ - if (fdset == NULL || fdset->nfds < 1 || fdset->fds == NULL) { - return -1; - } - - /* Initialize pointers. */ - fdset->polled = 0; - fdset->begin = 0; - - /* Poll for events. */ - int ret = poll(fdset->fds, fdset->nfds, timeout); - if (ret < 0) { - return -1; - } - - /* Set pointers for iterating. */ - fdset->polled = ret; - fdset->begin = 0; - return ret; -} - -int fdset_poll_begin(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL) { - return -1; - } - - /* Find first. */ - it->pos = 0; - return fdset_next(fdset, it); -} - -int fdset_poll_end(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL || fdset->nfds < 1) { - return -1; - } - - /* Check for polled events. */ - if (fdset->polled < 1) { - it->fd = -1; - it->pos = 0; - return -1; - } - - /* Trace last matching item from the end. */ - struct pollfd* pfd = fdset->fds + fdset->nfds - 1; - while (pfd != fdset->fds) { - if (pfd->events & pfd->revents) { - it->fd = pfd->fd; - it->pos = pfd - fdset->fds; - return 0; - } - } - - /* No end found, ends on the beginning. */ - it->fd = -1; - it->pos = 0; - return -1; -} - -int fdset_poll_next(fdset_t *fdset, fdset_it_t *it) -{ - if (fdset == NULL || it == NULL || fdset->nfds < 1) { - return -1; - } - - /* Find next with matching flags. */ - for (; it->pos < fdset->nfds; ++it->pos) { - struct pollfd* pfd = fdset->fds + it->pos; - if (pfd->events & pfd->revents) { - it->fd = pfd->fd; - it->events = pfd->revents; - ++it->pos; /* Next will start after current. */ - return 0; - } - } - - /* No matching event found. */ - it->fd = -1; - it->pos = 0; - return -1; -} - -const char* fdset_poll_method() -{ - return "poll"; -} - -/* Package APIs. */ -struct fdset_backend_t FDSET_POLL = { - .fdset_new = fdset_poll_new, - .fdset_destroy = fdset_poll_destroy, - .fdset_add = fdset_poll_add, - .fdset_remove = fdset_poll_remove, - .fdset_wait = fdset_poll_wait, - .fdset_begin = fdset_poll_begin, - .fdset_end = fdset_poll_end, - .fdset_next = fdset_poll_next, - .fdset_method = fdset_poll_method -}; - -#endif diff --git a/src/common/fdset_poll.h b/src/common/fdset_poll.h deleted file mode 100644 index 68e9e69097f339b1d0e8ce8e591f8b288d31cf68..0000000000000000000000000000000000000000 --- a/src/common/fdset_poll.h +++ /dev/null @@ -1,132 +0,0 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - - 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 3 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -/*! - * \file fdset_poll.h - * - * \author Marek Vavrusa <marek.vavrusa@nic.cz> - * - * \brief Wrapper for poll I/O multiplexing. - * - * \addtogroup common_lib - * @{ - */ - -#ifndef _KNOTD_FDSET_POLL_H_ -#define _KNOTD_FDSET_POLL_H_ - -#include "fdset.h" - -/*! - * \brief Create new fdset. - * - * POSIX poll() backend. - * - * \retval Pointer to initialized FDSET structure if successful. - * \retval NULL on error. - */ -fdset_t *fdset_poll_new(); - -/*! - * \brief Destroy FDSET. - * - * \retval 0 if successful. - * \retval -1 on error. - */ -int fdset_poll_destroy(fdset_t * fdset); - -/*! - * \brief Add file descriptor to watched set. - * - * \param fdset Target set. - * \param fd Added file descriptor. - * \param events Mask of watched events. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_poll_add(fdset_t *fdset, int fd, int events); - -/*! - * \brief Remove file descriptor from watched set. - * - * \param fdset Target set. - * \param fd File descriptor to be removed. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_poll_remove(fdset_t *fdset, int fd); - -/*! - * \brief Poll set for new events. - * - * \param fdset Target set. - * \param timeout Timeout (OS_EV_FOREVER, OS_EV_NOWAIT or value in miliseconds). - * - * \retval Number of events if successful. - * \retval -1 on errors. - */ -int fdset_poll_wait(fdset_t *fdset, int timeout); - -/*! - * \brief Set event iterator to the beginning of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_poll_begin(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Set event iterator to the end of last polled events. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_poll_end(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Set event iterator to the next event. - * - * Event iterator fd will be set to -1 if next event doesn't exist. - * - * \param fdset Target set. - * \param it Event iterator. - * - * \retval 0 if successful. - * \retval -1 on errors. - */ -int fdset_poll_next(fdset_t *fdset, fdset_it_t *it); - -/*! - * \brief Returned name of poll method. - * - * \retval Name if successful. - * \retval NULL if no method was loaded (shouldn't happen). - */ -const char* fdset_poll_method(); - -/*! \brief Exported API. */ -extern struct fdset_backend_t FDSET_POLL; - -#endif /* _KNOTD_FDSET_POLL_H_ */ - -/*! @} */ diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index 8cc0ec4a3ee32de8e95c82452ffbde6754dc83ed..9830c04a29223bdf36b8d86e29f3eeb2a1911af1 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -249,7 +249,8 @@ static int conf_process(conf_t *conf) } /* Default parallel transfers. */ - if (conf->xfers <= 0) conf->xfers = CONFIG_XFERS; + if (conf->xfers <= 0) + conf->xfers = CONFIG_XFERS; // Postprocess zones int ret = KNOT_EOK; diff --git a/src/knot/main.c b/src/knot/main.c index 685d5f247658f5a795ca85e7349262c189d2372d..610b21f2a6edb34ae65a8b2b258eb44fa174d57e 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -327,7 +327,7 @@ int main(int argc, char **argv) pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL); /* Bind to control interface. */ - uint8_t buf[65535]; /*! \todo #2035 should be on heap */ + uint8_t buf[SOCKET_MTU_SZ]; size_t buflen = sizeof(buf); int remote = -1; if (conf()->ctl.iface != NULL) { diff --git a/src/knot/server/server.c b/src/knot/server/server.c index cef38012dc7decf124479344729f124f8af2bb4a..7244d3c019a66060c90098fb4fcd76ba99e0f82f 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -629,18 +629,15 @@ int server_conf_hook(const struct conf_t *conf, void *data) return ret; } -ref_t *server_set_ifaces(server_t *s, fdset_t **fds, int *count, int type) +ref_t *server_set_ifaces(server_t *s, fdset_t *fds, int type) { iface_t *i = NULL; - *count = 0; rcu_read_lock(); - fdset_destroy(*fds); - *fds = fdset_new(); + fdset_clear(fds); if (s->ifaces) { WALK_LIST(i, s->ifaces->l) { - fdset_add(*fds, i->fd[type], OS_EV_READ); - *count += 1; + fdset_add(fds, i->fd[type], POLLIN, NULL); } } diff --git a/src/knot/server/server.h b/src/knot/server/server.h index 84a32e3f531eae025be934f7deca62866610fdf4..a766b0b14a4362ef339ca8df4577b628e1d138d3 100644 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h @@ -236,11 +236,10 @@ int server_conf_hook(const struct conf_t *conf, void *data); * \brief Update fdsets from current interfaces list. * \param s Server. * \param fds Filedescriptor set. - * \param count Number of ifaces (will be set to N). * \param type I/O type (UDP/TCP). * \return new interface list */ -ref_t *server_set_ifaces(server_t *s, fdset_t **fds, int *count, int type); +ref_t *server_set_ifaces(server_t *s, fdset_t *fds, int type); #endif // _KNOTD_SERVER_H_ diff --git a/src/knot/server/socket.h b/src/knot/server/socket.h index 1849f1d9d8ddd2ca84851afff2fb1a6e510c7773..371977a3e8975cb47fb6a2b126fd2f2279bf43d5 100644 --- a/src/knot/server/socket.h +++ b/src/knot/server/socket.h @@ -39,9 +39,7 @@ #include "common/sockaddr.h" /*! \brief Socket-related constants. */ -typedef enum { - SOCKET_MTU_SZ = 65535, /*!< Maximum MTU size. */ -} socket_const_t; +#define SOCKET_MTU_SZ 65535 /*!< Maximum MTU size. */ /*! * \brief Create socket. diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index f8fe90bb4caa14c36d5f641e376b287aa8677465..92eb40209cae1b35f0f8a72d2309fe0f7e69e07c 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -39,13 +39,10 @@ #include "libknot/nameserver/name-server.h" #include "libknot/util/wire.h" -/* Defines */ -#define TCP_BUFFER_SIZE 65535 /*! Do not change, as it is used for maximum DNS/TCP packet size. */ - /*! \brief TCP worker data. */ typedef struct tcp_worker_t { iohandler_t *ioh; /*!< Shortcut to I/O handler. */ - fdset_t *fdset; /*!< File descriptor set. */ + fdset_t set; /*!< File descriptor set. */ int pipe[2]; /*!< Master-worker signalization pipes. */ } tcp_worker_t; @@ -83,17 +80,19 @@ static int tcp_reply(int fd, uint8_t *qbuf, size_t resp_len) } /*! \brief Sweep TCP connection. */ -static void tcp_sweep(fdset_t *set, int fd, void* data) +static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data) { UNUSED(data); + assert(set && i < set->n && i >= 0); + int fd = set->pfd[i].fd; char r_addr[SOCKADDR_STRLEN] = { '\0' }; int r_port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); if (getpeername(fd, (struct sockaddr*)&addr, &len) < 0) { dbg_net("tcp: sweep getpeername() on invalid socket=%d\n", fd); - return; + return FDSET_SWEEP; } /* Translate */ @@ -111,8 +110,8 @@ static void tcp_sweep(fdset_t *set, int fd, void* data) log_server_notice("Connection with '%s@%d' was terminated due to " "inactivity.\n", r_addr, r_port); - fdset_remove(set, fd); close(fd); + return FDSET_SWEEP; } /*! @@ -327,10 +326,8 @@ int tcp_accept(int fd) tcp_worker_t* tcp_worker_create() { tcp_worker_t *w = malloc(sizeof(tcp_worker_t)); - if (w == NULL) { - dbg_net("tcp: out of memory when creating worker\n"); + if (w == NULL) return NULL; - } /* Create signal pipes. */ memset(w, 0, sizeof(tcp_worker_t)); @@ -340,16 +337,14 @@ tcp_worker_t* tcp_worker_create() } /* Create fdset. */ - w->fdset = fdset_new(); - if (!w->fdset) { + if (fdset_init(&w->set, FDSET_INIT_SIZE) != KNOT_EOK) { close(w->pipe[0]); close(w->pipe[1]); free(w); return NULL; } - fdset_add(w->fdset, w->pipe[0], OS_EV_READ); - + fdset_add(&w->set, w->pipe[0], POLLIN, NULL); return w; } @@ -359,8 +354,8 @@ void tcp_worker_free(tcp_worker_t* w) return; } - /* Destroy fdset. */ - fdset_destroy(w->fdset); + /* Clear fdset. */ + fdset_clear(&w->set); /* Close pipe write end and worker. */ close(w->pipe[0]); @@ -394,15 +389,21 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen) int uncork = setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)); #endif + /* Flags. */ + int flags = 0; +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + /* Send message size. */ unsigned short pktsize = htons(msglen); - int sent = send(fd, &pktsize, sizeof(pktsize), 0); + int sent = send(fd, &pktsize, sizeof(pktsize), flags); if (sent < 0) { return KNOT_ERROR; } /* Send message data. */ - sent = send(fd, msg, msglen, 0); + sent = send(fd, msg, msglen, flags); if (sent < 0) { return KNOT_ERROR; } @@ -421,9 +422,15 @@ int tcp_send(int fd, uint8_t *msg, size_t msglen) int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr) { + /* Flags. */ + int flags = MSG_WAITALL; +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + /* Receive size. */ unsigned short pktsize = 0; - int n = recv(fd, &pktsize, sizeof(unsigned short), MSG_WAITALL); + int n = recv(fd, &pktsize, sizeof(unsigned short), flags); if (n < 0) { if (errno == EAGAIN) { return KNOT_EAGAIN; @@ -455,7 +462,7 @@ int tcp_recv(int fd, uint8_t *buf, size_t len, sockaddr_t *addr) } /* Receive payload. */ - n = recv(fd, buf, pktsize, MSG_WAITALL); + n = recv(fd, buf, pktsize, flags); if (n < 0) { if (errno == EAGAIN) { return KNOT_EAGAIN; @@ -481,23 +488,22 @@ int tcp_loop_master(dthread_t *thread) tcp_worker_t **workers = h->data; /* Prepare structures for bound sockets. */ - fdset_it_t it; - fdset_t *fds = NULL; ref_t *ref = NULL; - int if_cnt = 0; + fdset_t set; + fdset_init(&set, conf()->ifaces_count); /* Accept connections. */ - int id = 0; - dbg_net("tcp: created 1 master with %d workers, backend is '%s' \n", - unit->size - 1, fdset_method()); + int id = 0, ret = 0; + dbg_net("tcp: created 1 master with %d workers\n", unit->size - 1); for(;;) { /* Check handler state. */ if (knot_unlikely(st->s & ServerReload)) { st->s &= ~ServerReload; ref_release(ref); - ref = server_set_ifaces(h->server, &fds, &if_cnt, IO_TCP); - if (if_cnt == 0) break; + ref = server_set_ifaces(h->server, &set, IO_TCP); + if (set.n == 0) /* Terminate on zero interfaces. */ + break; } /* Check for cancellation. */ @@ -506,35 +512,34 @@ int tcp_loop_master(dthread_t *thread) } /* Wait for events. */ - int nfds = fdset_wait(fds, OS_EV_FOREVER); + int nfds = poll(set.pfd, set.n, -1); if (nfds <= 0) { - if (errno == EINTR) continue; + if (errno == EINTR) + continue; break; } - fdset_begin(fds, &it); - while(nfds > 0) { - /* Accept client. */ - int client = tcp_accept(it.fd); - if (client > -1) { - /* Add to worker in RR fashion. */ - if (write(workers[id]->pipe[1], &client, sizeof(int)) < 0) { - dbg_net("tcp: failed to register fd=%d to worker=%d\n", - client, id); - close(client); - continue; - } - id = get_next_rr(id, unit->size - 1); - } + for (unsigned i = 0; nfds > 0 && i < set.n; ++i) { + /* Skip inactive. */ + if (!(set.pfd[i].revents & POLLIN)) + continue; - if (fdset_next(fds, &it) != 0) { - break; - } + /* Accept client. */ + --nfds; /* One less active event. */ + int client = tcp_accept(set.pfd[i].fd); + if (client < 0) + continue; + + /* Add to worker in RR fashion. */ + id = get_next_rr(id, unit->size - 1); + ret = write(workers[id]->pipe[1], &client, sizeof(int)); + if (ret < 0) + close(client); } } dbg_net("tcp: master thread finished\n"); - fdset_destroy(fds); + fdset_clear(&set); ref_release(ref); return KNOT_EOK; @@ -542,18 +547,6 @@ int tcp_loop_master(dthread_t *thread) int tcp_loop_worker(dthread_t *thread) { - tcp_worker_t *w = thread->data; - if (!w) { - return KNOT_EINVAL; - } - - /* Allocate buffer for requests. */ - uint8_t *qbuf = malloc(TCP_BUFFER_SIZE); - if (qbuf == NULL) { - dbg_net("tcp: failed to allocate buffers for TCP worker\n"); - return KNOT_EINVAL; - } - /* Drop all capabilities on workers. */ #ifdef HAVE_CAP_NG_H if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) { @@ -562,25 +555,29 @@ int tcp_loop_worker(dthread_t *thread) } #endif /* HAVE_CAP_NG_H */ - /* Next sweep time. */ + uint8_t *qbuf = malloc(SOCKET_MTU_SZ); + tcp_worker_t *w = thread->data; + if (w == NULL || qbuf == NULL) { + free(qbuf); + return KNOT_EINVAL; + } + + /* Accept clients. */ + dbg_net("tcp: worker %p started\n", w); + fdset_t *set = &w->set; timev_t next_sweep; time_now(&next_sweep); next_sweep.tv_sec += TCP_SWEEP_INTERVAL; - - /* Accept clients. */ - dbg_net_verb("tcp: worker %p started\n", w); for (;;) { /* Cancellation point. */ - if (dt_is_cancelled(thread)) { + if (dt_is_cancelled(thread)) break; - } /* Wait for events. */ - int nfds = fdset_wait(w->fdset, (TCP_SWEEP_INTERVAL * 1000)/2); - if (nfds < 0) { + int nfds = poll(set->pfd, set->n, TCP_SWEEP_INTERVAL * 1000); + if (nfds < 0) continue; - } /* Establish timeouts. */ rcu_read_lock(); @@ -589,57 +586,49 @@ int tcp_loop_worker(dthread_t *thread) rcu_read_unlock(); /* Process incoming events. */ - dbg_net_verb("tcp: worker %p registered %d events\n", - w, nfds); - fdset_it_t it; - fdset_begin(w->fdset, &it); - while(nfds > 0) { - - /* Handle incoming clients. */ - if (it.fd == w->pipe[0]) { - int client = 0; - if (read(it.fd, &client, sizeof(int)) < 0) { - continue; - } + unsigned i = 0; + while (nfds > 0 && i < set->n) { + + if (!(set->pfd[i].revents & set->pfd[i].events)) { + /* Skip inactive. */ + ++i; + continue; + } else { + /* One less active event. */ + --nfds; + } - dbg_net_verb("tcp: worker %p registered " - "client %d\n", - w, client); - fdset_add(w->fdset, client, OS_EV_READ); - fdset_set_watchdog(w->fdset, client, max_hs); - dbg_net("tcp: watchdog for fd=%d set to %ds\n", - client, max_hs); + int fd = set->pfd[i].fd; + if (fd == w->pipe[0]) { + /* Register incoming TCP connection. */ + int client, next_id; + if (read(fd, &client, sizeof(int)) == sizeof(int)) { + next_id = fdset_add(set, client, POLLIN, NULL); + fdset_set_watchdog(set, next_id, max_hs); + } } else { - /* Handle other events. */ - int ret = tcp_handle(w, it.fd, qbuf, - TCP_BUFFER_SIZE); + /* Process query over TCP. */ + int ret = tcp_handle(w, fd, qbuf, SOCKET_MTU_SZ); if (ret == KNOT_EOK) { - fdset_set_watchdog(w->fdset, it.fd, - max_idle); - dbg_net("tcp: watchdog for fd=%d " - "set to %ds\n", - it.fd, max_idle); + /* Update socket activity timer. */ + fdset_set_watchdog(set, i, max_idle); } - /*! \todo Refactor to allow erase on iterator.*/ if (ret == KNOT_ECONNREFUSED) { - fdset_remove(w->fdset, it.fd); - close(it.fd); - break; + fdset_remove(set, i); + close(fd); + continue; /* Stay on the same index. */ } - } - /* Check if next exists. */ - if (fdset_next(w->fdset, &it) != 0) { - break; - } + /* Next active. */ + ++i; } /* Sweep inactive. */ timev_t now; if (time_now(&now) == 0) { if (now.tv_sec >= next_sweep.tv_sec) { - fdset_sweep(w->fdset, &tcp_sweep, NULL); + fdset_sweep(set, &tcp_sweep, NULL); memcpy(&next_sweep, &now, sizeof(next_sweep)); next_sweep.tv_sec += TCP_SWEEP_INTERVAL; } @@ -648,7 +637,7 @@ int tcp_loop_worker(dthread_t *thread) /* Stop whole unit. */ free(qbuf); - dbg_net_verb("tcp: worker %p finished\n", w); + dbg_net("tcp: worker %p finished\n", w); return KNOT_EOK; } diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c index c7c1abfcca05e5157395c61474f7eadc31177fde..39d012e9ad2a9488765513ca1c83189807dc9cbd 100644 --- a/src/knot/server/xfr-handler.c +++ b/src/knot/server/xfr-handler.c @@ -44,9 +44,9 @@ #include "libknot/rrset.h" /* Constants */ -#define XFR_CHUNKLEN 3 /*! Number of requests assigned in a single pass. */ +#define XFR_MAX_TASKS 1024 /*! Maximum pending tasks. */ +#define XFR_CHUNKLEN 16 /*! Number of requests assigned in a single pass. */ #define XFR_SWEEP_INTERVAL 2 /*! [seconds] between sweeps. */ -#define XFR_BUFFER_SIZE 65535 /*! Do not change this - maximum value for UDP packet length. */ #define XFR_MSG_DLTTR 9 /*! Index of letter differentiating IXFR/AXFR in log msg. */ /* Messages */ @@ -70,6 +70,29 @@ static knot_lookup_table_t xfr_result_table[] = { { XFR_TYPE_AIN, NULL } }; +/* Limits. */ +static bool xfr_pending_incr(xfrhandler_t *xfr) +{ + bool ret = false; + pthread_mutex_lock(&xfr->pending_mx); + rcu_read_lock(); + if (xfr->pending < conf()->xfers) { + ++xfr->pending; + ret = true; + } + rcu_read_unlock(); + pthread_mutex_unlock(&xfr->pending_mx); + + return ret; +} + +static void xfr_pending_decr(xfrhandler_t *xfr) +{ + pthread_mutex_lock(&xfr->pending_mx); + --xfr->pending; + pthread_mutex_unlock(&xfr->pending_mx); +} + /* I/O wrappers */ static int xfr_send_tcp(int fd, sockaddr_t *addr, uint8_t *msg, size_t msglen) @@ -84,28 +107,6 @@ static int xfr_recv_tcp(int fd, sockaddr_t *addr, uint8_t *buf, size_t buflen) static int xfr_recv_udp(int fd, sockaddr_t *addr, uint8_t *buf, size_t buflen) { return recvfrom(fd, buf, buflen, 0, (struct sockaddr *)addr, &addr->len); } -/* Context fetching. */ - -static knot_ns_xfr_t* xfr_task_get(xfrworker_t *w, int fd) -{ - value_t *val = ahtable_tryget(w->pool.t, (const char*)&fd, sizeof(int)); - if (!val) return NULL; - return *val; -} - -static void xfr_task_set(xfrworker_t *w, int fd, knot_ns_xfr_t *rq) -{ - fdset_add(w->pool.fds, fd, OS_EV_READ); - value_t *val = ahtable_get(w->pool.t, (const char*)&fd, sizeof(int)); - *val = rq; -} - -static void xfr_task_clear(xfrworker_t *w, int fd) -{ - ahtable_del(w->pool.t, (const char*)&fd, sizeof(int)); - fdset_remove(w->pool.fds, fd); -} - /*! \brief Wrapper function for answering AXFR/OUT. */ static int xfr_answer_axfr(knot_nameserver_t *ns, knot_ns_xfr_t *xfr) { @@ -241,10 +242,8 @@ static int xfr_task_setsig(knot_ns_xfr_t *rq, knot_tsig_key_t *key) static int xfr_task_connect(knot_ns_xfr_t *rq) { /* Create socket by type. */ - int stype = SOCK_DGRAM; - if (rq->flags & XFR_FLAG_TCP) { - stype = SOCK_STREAM; - } + int ret = 0; + int stype = (rq->flags & XFR_FLAG_TCP) ? SOCK_STREAM : SOCK_DGRAM; int fd = socket_create(sockaddr_family(&rq->addr), stype, 0); if (fd < 0) { return KNOT_ERROR; @@ -257,14 +256,22 @@ static int xfr_task_connect(knot_ns_xfr_t *rq) return KNOT_ERROR; } } + /* Connect if TCP. */ if (rq->flags & XFR_FLAG_TCP) { - if (connect(fd, (struct sockaddr *)&rq->addr, rq->addr.len) < 0) { + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + ; /* Go silently with blocking if it fails. */ + + ret = connect(fd, (struct sockaddr *)&rq->addr, rq->addr.len); + if (ret != 0 && errno != EINPROGRESS) { socket_close(fd); return KNOT_ECONNREFUSED; } } + /* Set up for UDP as well to trigger 'send query' event. */ + rq->flags |= XFR_FLAG_CONNECTING; + /* Store new socket descriptor. */ rq->session = fd; return KNOT_EOK; @@ -299,15 +306,12 @@ static void xfr_task_cleanup(knot_ns_xfr_t *rq) hattrie_clear(rq->lookup_tree); } -/*! \brief Close and free task. */ -static int xfr_task_close(xfrworker_t *w, int fd) +/*! \brief End task properly and free it. */ +static int xfr_task_close(knot_ns_xfr_t *rq) { - knot_ns_xfr_t *rq = xfr_task_get(w, fd); - if (!rq) return KNOT_ENOENT; - /* Update xfer state. */ + zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone); if (rq->type == XFR_TYPE_AIN || rq->type == XFR_TYPE_IIN) { - zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone); pthread_mutex_lock(&zd->lock); if (zd->xfr_in.state == XFR_PENDING) { zd->xfr_in.state = XFR_IDLE; @@ -315,15 +319,25 @@ static int xfr_task_close(xfrworker_t *w, int fd) pthread_mutex_unlock(&zd->lock); } + /* Reschedule failed bootstrap. */ + if (rq->type == XFR_TYPE_AIN && !knot_zone_contents(rq->zone)) { + int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand(); + event_t *ev = zd->xfr_in.timer; + if (ev) { + evsched_cancel(ev->parent, ev); + evsched_schedule(ev->parent, ev, tmr_s); + } + log_zone_notice("%s Bootstrap failed, next attempt in %d seconds.\n", + rq->msg, tmr_s / 1000); + } + /* Close socket and free task. */ - xfr_task_clear(w, fd); xfr_task_free(rq); - socket_close(fd); return KNOT_EOK; } /*! \brief Timeout handler. */ -static int xfr_task_expire(fdset_t *fds, knot_ns_xfr_t *rq) +static int xfr_task_expire(fdset_t *set, int i, knot_ns_xfr_t *rq) { /* Fetch related zone (refcounted, no RCU). */ knot_zone_t *zone = (knot_zone_t *)rq->zone; @@ -335,7 +349,7 @@ static int xfr_task_expire(fdset_t *fds, knot_ns_xfr_t *rq) case XFR_TYPE_NOTIFY: if ((long)--rq->data > 0) { /* Retries */ notify_create_request(contents, rq->wire, &rq->wire_size); - fdset_set_watchdog(fds, rq->session, NOTIFY_TIMEOUT); + fdset_set_watchdog(set, i, NOTIFY_TIMEOUT); rq->send(rq->session, &rq->addr, rq->wire, rq->wire_size); log_zone_info("%s Query issued (serial %u).\n", rq->msg, knot_zone_serial(contents)); @@ -370,14 +384,6 @@ static int xfr_task_start(knot_ns_xfr_t *rq) return KNOT_ECONNREFUSED; } - /* Connect to remote. */ - if (rq->session <= 0) { - ret = xfr_task_connect(rq); - if (ret != KNOT_EOK) { - return ret; - } - } - /* Prepare TSIG key if set. */ int add_tsig = 0; if (rq->tsig_key) { @@ -437,20 +443,13 @@ static int xfr_task_start(knot_ns_xfr_t *rq) return KNOT_EOK; } -static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, size_t buflen) +static int xfr_task_is_transfer(knot_ns_xfr_t *rq) { - /* Check if not already processing, zone is refcounted. */ - zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone); - - /* Check if the zone is not discarded. */ - if (!zd || knot_zone_flags(rq->zone) & KNOT_ZONE_DISCARDED) { - dbg_xfr_verb("xfr: request on a discarded zone, ignoring\n"); - return KNOT_EINVAL; - } - - /* Update XFR message prefix. */ - xfr_task_setmsg(rq, NULL); + return rq->type == XFR_TYPE_AIN || rq->type == XFR_TYPE_IIN; +} +static void xfr_async_setbuf(knot_ns_xfr_t *rq, uint8_t *buf, size_t buflen) +{ /* Update request. */ rq->wire = buf; rq->wire_size = buflen; @@ -461,17 +460,78 @@ static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, siz rq->send = &xfr_send_tcp; rq->recv = &xfr_recv_tcp; } +} + +static int xfr_async_start(fdset_t *set, knot_ns_xfr_t *rq) +{ + /* Update XFR message prefix. */ + int ret = KNOT_EOK; + xfr_task_setmsg(rq, NULL); + + /* Connect to remote. */ + if (rq->session <= 0) + ret = xfr_task_connect(rq); + + /* Add to set. */ + if (ret == KNOT_EOK) { + unsigned flags = POLLIN; + if (rq->flags & XFR_FLAG_CONNECTING) + flags = POLLOUT; + int next_id = fdset_add(set, rq->session, flags, rq); + if (next_id >= 0) { + /* Set default connection timeout. */ + rcu_read_lock(); + fdset_set_watchdog(set, next_id, conf()->max_conn_reply); + rcu_read_unlock(); + } else { + /* Or refuse if failed. */ + ret = KNOT_ECONNREFUSED; + } + } + + return ret; +} + +static int xfr_async_state(knot_ns_xfr_t *rq) +{ + /* Check socket status. */ + int err = EINVAL; + socklen_t len = sizeof(int); + if (getsockopt(rq->session, SOL_SOCKET, SO_ERROR, &err, &len) < 0) + return KNOT_ERROR; + if (err != 0) + return knot_map_errno(err); + return KNOT_EOK; +} + +static int xfr_async_finish(fdset_t *set, unsigned id) +{ + /* Drop back to synchronous mode. */ + int ret = KNOT_EOK; + knot_ns_xfr_t *rq = (knot_ns_xfr_t *)set->ctx[id]; + if ((ret = xfr_async_state(rq)) == KNOT_EOK) { + rq->flags &= ~XFR_FLAG_CONNECTING; + set->pfd[id].events = POLLIN; + if (fcntl(set->pfd[id].fd, F_SETFL, 0) < 0) + ; + } + + /* Check if the zone is not discarded. */ + zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone); + if (!zd || knot_zone_flags(rq->zone) & KNOT_ZONE_DISCARDED) { + dbg_xfr_verb("xfr: request on a discarded zone, ignoring\n"); + return KNOT_EINVAL; + } /* Handle request. */ dbg_xfr("%s processing request type '%d'\n", rq->msg, rq->type); - int ret = xfr_task_start(rq); + ret = xfr_task_start(rq); const char *msg = knot_strerror(ret); knot_lookup_table_t *xd = knot_lookup_by_id(xfr_result_table, rq->type); if (xd && ret == KNOT_EOK) { msg = xd->name; } - int bootstrap_fail = 0; switch(rq->type) { case XFR_TYPE_AIN: case XFR_TYPE_IIN: @@ -480,34 +540,26 @@ static int xfr_task_process(xfrworker_t *w, knot_ns_xfr_t* rq, uint8_t *buf, siz pthread_mutex_lock(&zd->lock); zd->xfr_in.state = XFR_IDLE; pthread_mutex_unlock(&zd->lock); - bootstrap_fail = !knot_zone_contents(rq->zone); } break; - case XFR_TYPE_NOTIFY: /* Send on first timeout <0,5>s. */ - fdset_set_watchdog(w->pool.fds, rq->session, (int)(tls_rand() * 5)); - xfr_task_set(w, rq->session, rq); + case XFR_TYPE_NOTIFY: + /* This is a bit of a hack to adapt NOTIFY lifetime tracking. + * When NOTIFY event enters handler, it shouldn't be sent immediately. + * To accomodate for this, <0, 5>s random delay is set on + * event startup, so the first query fires when this timer + * expires. */ + fdset_set_watchdog(set, id, (int)(tls_rand() * 5)); return KNOT_EOK; case XFR_TYPE_SOA: case XFR_TYPE_FORWARD: - fdset_set_watchdog(w->pool.fds, rq->session, conf()->max_conn_reply); + fdset_set_watchdog(set, id, conf()->max_conn_reply); break; default: break; } if (ret == KNOT_EOK) { - xfr_task_set(w, rq->session, rq); log_server_info("%s %s.\n", rq->msg, msg); - } else if (bootstrap_fail){ - int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand(); - event_t *ev = zd->xfr_in.timer; - if (ev) { - evsched_cancel(ev->parent, ev); - evsched_schedule(ev->parent, ev, tmr_s); - } - log_zone_notice("%s Bootstrap failed, next " - "attempt in %d seconds.\n", - rq->msg, tmr_s / 1000); } else { log_server_error("%s %s.\n", rq->msg, msg); } @@ -745,16 +797,11 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq) /* Only for successful xfers. */ if (ret > 0) { ret = xfr_task_finalize(w, rq); - if (ret != KNOT_EOK && !knot_zone_contents(rq->zone)) { - - /* AXFR bootstrap timeout. */ - zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone); - int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand(); - zd->xfr_in.bootstrap_retry = tmr_s; - log_zone_info("%s Next attempt to bootstrap " - "in %d seconds.\n", - rq->msg, tmr_s / 1000); - } else if (ret == KNOT_EBUSY && rq->type == XFR_TYPE_IIN) { + + /* EBUSY on incremental transfer has a special meaning and + * is caused by a journal not able to free up space for incoming + * transfer, thus forcing to start a new full zone transfer. */ + if (ret == KNOT_EBUSY && rq->type == XFR_TYPE_IIN) { return xfr_start_axfr(w, rq, diff_nospace_msg); } else { @@ -763,7 +810,7 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq) } /* Update REFRESH/RETRY */ - zones_schedule_refresh(rq->zone, 0); + zones_schedule_refresh(rq->zone, REFRESH_DEFAULT); ret = KNOT_ECONNREFUSED; /* Disconnect */ } @@ -801,37 +848,36 @@ static int xfr_process_event(xfrworker_t *w, knot_ns_xfr_t *rq) } } -/*! \brief Sweep non-replied connection. */ -static void xfr_sweep(fdset_t *set, int fd, void *data) +/*! \brief Sweep inactive connection. */ +static enum fdset_sweep_state xfr_sweep(fdset_t *set, int i, void *data) { - dbg_xfr("xfr: sweeping fd=%d\n", fd); - if (!set || !data) { - dbg_xfr("xfr: invalid sweep operation on NULL worker or set\n"); - return; - } - xfrworker_t *w = (xfrworker_t *)data; - knot_ns_xfr_t *rq = xfr_task_get(w, fd); - if (!rq) { - dbg_xfr("xfr: NULL data to sweep\n"); - return; - } + assert(set && i < set->n && i >= 0); - /* Skip non-sweepable types. */ + knot_ns_xfr_t *rq = set->ctx[i]; + xfrhandler_t *xfr = (xfrhandler_t *)data; + + /* Expire only UDP requests. */ int ret = KNOT_ECONNREFUSED; switch(rq->type) { case XFR_TYPE_SOA: case XFR_TYPE_NOTIFY: case XFR_TYPE_FORWARD: - ret = xfr_task_expire(set, rq); + ret = xfr_task_expire(set, i, rq); break; default: break; } + /* Close if not valid anymore. */ if (ret != KNOT_EOK) { - xfr_task_close(w, fd); - --w->pending; + if (xfr_task_is_transfer(rq)) + xfr_pending_decr(xfr); + xfr_task_close(rq); + socket_close(set->pfd[i].fd); + return FDSET_SWEEP; } + + return FDSET_KEEP; } /*! \brief Check TSIG if exists. */ @@ -969,7 +1015,7 @@ int xfr_worker(dthread_t *thread) xfrhandler_t *xfr = w->master; /* Buffer for answering. */ - size_t buflen = XFR_BUFFER_SIZE; + size_t buflen = SOCKET_MTU_SZ; uint8_t* buf = malloc(buflen); if (buf == NULL) { dbg_xfr("xfr: failed to allocate buffer for XFR worker\n"); @@ -981,84 +1027,113 @@ int xfr_worker(dthread_t *thread) time_now(&next_sweep); next_sweep.tv_sec += XFR_SWEEP_INTERVAL; - int limit = XFR_CHUNKLEN * 3; - if (conf() && conf()->xfers > 0) { - limit = conf()->xfers; + /* Approximate thread capacity limits. */ + unsigned threads = w->master->unit->size; + unsigned thread_capacity = XFR_MAX_TASKS / threads; + + /* Set of connections. */ + fdset_t set; + int ret = fdset_init(&set, thread_capacity); + if (ret != KNOT_EOK) { + free(buf); + return ret; } - unsigned thread_capacity = limit / w->master->unit->size; - if (thread_capacity < 1) thread_capacity = 1; - w->pool.fds = fdset_new(); - w->pool.t = ahtable_create(); - w->pending = 0; /* Accept requests. */ - int ret = 0; dbg_xfr_verb("xfr: worker=%p starting\n", w); for (;;) { /* Populate pool with new requests. */ - if (w->pending <= thread_capacity) { - pthread_mutex_lock(&xfr->mx); - unsigned was_pending = w->pending; - while (!EMPTY_LIST(xfr->queue)) { - knot_ns_xfr_t *rq = HEAD(xfr->queue); - rem_node(&rq->n); + unsigned newconns = 0; + for (;;) { + /* Do not exceed thread capacity. */ + if (set.n >= thread_capacity || newconns > XFR_CHUNKLEN) + break; - /* Unlock queue and process request. */ + /* Tak first request. */ + pthread_mutex_lock(&xfr->mx); + if (EMPTY_LIST(xfr->queue)) { pthread_mutex_unlock(&xfr->mx); - ret = xfr_task_process(w, rq, buf, buflen); - if (ret == KNOT_EOK) ++w->pending; - else xfr_task_free(rq); - pthread_mutex_lock(&xfr->mx); + break; + } + - if (w->pending - was_pending > XFR_CHUNKLEN) - break; + /* Limit number of transfers. */ + knot_ns_xfr_t *rq = HEAD(xfr->queue); + unsigned is_transfer = xfr_task_is_transfer(rq); + if (is_transfer && !xfr_pending_incr(xfr)) { + pthread_mutex_unlock(&xfr->mx); + break; } + + rem_node(&rq->n); pthread_mutex_unlock(&xfr->mx); + + /* Start asynchronous connect. */ + xfr_async_setbuf(rq, buf, buflen); + if (xfr_async_start(&set, rq) != KNOT_EOK) { + if (is_transfer) + xfr_pending_decr(xfr); + xfr_task_close(rq); + break; + } + + ++newconns; } /* Check pending threads. */ - if (dt_is_cancelled(thread) || w->pending == 0) { + if (dt_is_cancelled(thread) || set.n == 0) { break; } /* Poll fdset. */ - int nfds = fdset_wait(w->pool.fds, (XFR_SWEEP_INTERVAL/2) * 1000); + int nfds = poll(set.pfd, set.n, XFR_SWEEP_INTERVAL * 1000); if (nfds < 0) { - if (errno == EINTR) continue; + if (errno == EINTR) + continue; break; } /* Iterate fdset. */ - fdset_it_t it; - fdset_begin(w->pool.fds, &it); - while(nfds > 0) { - - /* Find data. */ - knot_ns_xfr_t *rq = xfr_task_get(w, it.fd); - dbg_xfr_verb("xfr: worker=%p processing event on " - "fd=%d data=%p.\n", - w, it.fd, rq); - if (rq) { + unsigned i = 0; + while (nfds > 0 && i < set.n && !dt_is_cancelled(thread)) { + + if (!(set.pfd[i].revents & set.pfd[i].events)) { + /* Skip inactive. */ + ++i; + continue; + } else { + /* One less active event. */ + --nfds; + } + + /* Process pending tasks. */ + knot_ns_xfr_t *rq = (knot_ns_xfr_t *)set.ctx[i]; + if (rq->flags & XFR_FLAG_CONNECTING) { + ret = xfr_async_finish(&set, i); + } else { ret = xfr_process_event(w, rq); - if (ret != KNOT_EOK) { - xfr_task_close(w, it.fd); - --w->pending; - --it.pos; /* Reset iterator */ - } } - /* Next fd. */ - if (fdset_next(w->pool.fds, &it) < 0) { - break; + /* Check task state. */ + if (ret != KNOT_EOK) { + if (xfr_task_is_transfer(rq)) + xfr_pending_decr(xfr); + xfr_task_close(rq); + socket_close(set.pfd[i].fd); + fdset_remove(&set, i); + continue; /* Stay on the same index. */ } + + /* Next active. */ + ++i; } /* Sweep inactive. */ timev_t now; if (time_now(&now) == 0) { if (now.tv_sec >= next_sweep.tv_sec) { - fdset_sweep(w->pool.fds, &xfr_sweep, w); + fdset_sweep(&set, &xfr_sweep, xfr); memcpy(&next_sweep, &now, sizeof(next_sweep)); next_sweep.tv_sec += XFR_SWEEP_INTERVAL; } @@ -1066,22 +1141,17 @@ int xfr_worker(dthread_t *thread) } /* Cancel existing connections. */ - size_t keylen = 0; - ahtable_iter_t i; - ahtable_iter_begin(w->pool.t, &i, false); - while (!ahtable_iter_finished(&i)) { - int *key = (int *)ahtable_iter_key(&i, &keylen); - xfr_task_close(w, *key); - ahtable_iter_next(&i); - } - ahtable_iter_free(&i); - - /* Destroy data structures. */ - fdset_destroy(w->pool.fds); - ahtable_free(w->pool.t); - free(buf); + for (unsigned i = 0; i < set.n; ++i) { + knot_ns_xfr_t *rq = (knot_ns_xfr_t *)set.ctx[i]; + socket_close(set.pfd[i].fd); + if (xfr_task_is_transfer(rq)) + xfr_pending_decr(xfr); + xfr_task_free(rq); + } dbg_xfr_verb("xfr: worker=%p finished.\n", w); + fdset_clear(&set); + free(buf); return KNOT_EOK; } @@ -1115,6 +1185,7 @@ xfrhandler_t *xfr_create(size_t thrcount, knot_nameserver_t *ns) /* Create tasks structure and mutex. */ pthread_mutex_init(&xfr->mx, 0); + pthread_mutex_init(&xfr->pending_mx, 0); init_list(&xfr->queue); /* Assign worker threads. */ @@ -1133,6 +1204,7 @@ int xfr_free(xfrhandler_t *xfr) } /* Free RR mutex. */ + pthread_mutex_destroy(&xfr->pending_mx); pthread_mutex_destroy(&xfr->mx); /* Free pending queue. */ diff --git a/src/knot/server/xfr-handler.h b/src/knot/server/xfr-handler.h index 204260541ae4407e69b9c2233e7de0c0f8ffc245..f0b04875705c40605ff4d1f9abcdae0de6dd543a 100644 --- a/src/knot/server/xfr-handler.h +++ b/src/knot/server/xfr-handler.h @@ -48,11 +48,6 @@ enum xfrstate_t { */ typedef struct xfrworker_t { - struct { - ahtable_t *t; - fdset_t *fds; - } pool; - unsigned pending; struct xfrhandler_t *master; /*! \brief Worker master. */ } xfrworker_t; @@ -62,6 +57,8 @@ typedef struct xfrworker_t typedef struct xfrhandler_t { list queue; + unsigned pending; /*!< \brief Pending transfers. */ + pthread_mutex_t pending_mx; pthread_mutex_t mx; /*!< \brief Tasks synchronisation. */ knot_nameserver_t *ns; dt_unit_t *unit; /*!< \brief Threading unit. */ diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c index d1a3bf694e4d07f9339d37e0636a52836f1e67cb..eba8fc516e5fb1f46d48a3e0e73ffbd6803904a0 100644 --- a/src/knot/server/zones.c +++ b/src/knot/server/zones.c @@ -2495,7 +2495,7 @@ int zones_process_response(knot_nameserver_t *nameserver, /* No updates available. */ if (ret == 0) { - zones_schedule_refresh(zone, 0); + zones_schedule_refresh(zone, REFRESH_DEFAULT); rcu_read_unlock(); return KNOT_EUPTODATE; } @@ -2698,8 +2698,7 @@ int zones_ns_conf_hook(const struct conf_t *conf, void *data) /* REFRESH zones. */ for (unsigned i = 0; i < knot_zonedb_zone_count(ns->zone_db); ++i) { - /* Refresh new slave zones (almost) immediately. */ - zones_schedule_refresh(zones[i], tls_rand() * 500 + i/2); + zones_schedule_refresh(zones[i], 0); /* Now. */ zones_schedule_notify(zones[i]); } @@ -3177,7 +3176,7 @@ int zones_schedule_notify(knot_zone_t *zone) return KNOT_EOK; } -int zones_schedule_refresh(knot_zone_t *zone, unsigned time) +int zones_schedule_refresh(knot_zone_t *zone, int64_t time) { if (!zone || !zone->data) { return KNOT_EINVAL; @@ -3206,18 +3205,17 @@ int zones_schedule_refresh(knot_zone_t *zone, unsigned time) if (zd->xfr_in.has_master) { /* Schedule REFRESH timer. */ - uint32_t refresh_tmr = time; - if (refresh_tmr == 0) { - if (knot_zone_contents(zone)) { - refresh_tmr = zones_jitter(zones_soa_refresh(zone)); - } else { - refresh_tmr = zd->xfr_in.bootstrap_retry; - } + if (time < 0) { + if (knot_zone_contents(zone)) + time = zones_jitter(zones_soa_refresh(zone)); + else + time = zd->xfr_in.bootstrap_retry; } + zd->xfr_in.timer = evsched_schedule_cb(sch, zones_refresh_ev, - zone, refresh_tmr); + zone, time); dbg_zones("zone: REFRESH '%s' set to %u\n", - zd->conf->name, refresh_tmr); + zd->conf->name, time); zd->xfr_in.state = XFR_SCHED; } rcu_read_unlock(); diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h index 5045d50bd288108674ad93d05955c309747d8b87..2260e01a0819ae13f9db183cd41be66ecb0dc889 100644 --- a/src/knot/server/zones.h +++ b/src/knot/server/zones.h @@ -46,6 +46,10 @@ #define IXFR_DBSYNC_TIMEOUT (60*1000) /*!< Database sync timeout = 60s. */ #define AXFR_BOOTSTRAP_RETRY (30*1000) /*!< Interval between AXFR BS retries. */ +enum { + REFRESH_DEFAULT = -1 /* Use time value from zone structure. */ +}; + /*! * \brief Zone-related data. */ @@ -302,13 +306,13 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs, * REFRESH/RETRY/EXPIRE timers are updated according to SOA. * * \param zone Related zone. - * \param time Specific time or 0 for default. + * \param time Specific time or REFRESH_DEFAULT for default. * * \retval KNOT_EOK * \retval KNOT_EINVAL * \retval KNOT_ERROR */ -int zones_schedule_refresh(knot_zone_t *zone, unsigned time); +int zones_schedule_refresh(knot_zone_t *zone, int64_t time); /*! * \brief Schedule NOTIFY after zone update. diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h index 58ea212b9cc6a3722974c846a8c41eb3b444b658..6574539bbe5f8f5381641b0226a3a9959379d448 100644 --- a/src/libknot/nameserver/name-server.h +++ b/src/libknot/nameserver/name-server.h @@ -159,7 +159,8 @@ static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024; enum knot_ns_xfr_flag_t { XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */ XFR_FLAG_UDP = 1 << 1, /*!< XFR request is on UDP. */ - XFR_FLAG_AXFR_FINISHED = 1 << 2 /*!< Transfer is finished. */ + XFR_FLAG_AXFR_FINISHED = 1 << 2, /*!< Transfer is finished. */ + XFR_FLAG_CONNECTING = 1 << 3 /*!< In connecting phase. */ }; typedef enum knot_ns_transport { @@ -173,8 +174,8 @@ typedef enum knot_ns_transport { typedef enum knot_ns_xfr_type_t { /* DNS events. */ XFR_TYPE_AIN = 0, /*!< AXFR-IN request (start transfer). */ - XFR_TYPE_AOUT, /*!< AXFR-OUT request (incoming transfer). */ XFR_TYPE_IIN, /*!< IXFR-IN request (start transfer). */ + XFR_TYPE_AOUT, /*!< AXFR-OUT request (incoming transfer). */ XFR_TYPE_IOUT, /*!< IXFR-OUT request (incoming transfer). */ XFR_TYPE_SOA, /*!< Pending SOA request. */ XFR_TYPE_NOTIFY, /*!< Pending NOTIFY query. */ diff --git a/src/tests/common/fdset_tests.c b/src/tests/common/fdset_tests.c index d7b29fa52887fd7837b7e99c1da4253b1f38de83..e8b9d73ee309f90f75d19cfd6d59413d585d699d 100644 --- a/src/tests/common/fdset_tests.c +++ b/src/tests/common/fdset_tests.c @@ -98,22 +98,21 @@ static int fdset_tests_count(int argc, char *argv[]) static int fdset_tests_run(int argc, char *argv[]) { - diag("fdset: implements '%s'", fdset_method()); - /* 1. Create fdset. */ - fdset_t *set = fdset_new(); - ok(set != 0, "fdset: new"); + fdset_t set; + int ret = fdset_init(&set, 32); + ok(ret == 0, "fdset: init"); /* 2. Create pipe. */ int fds[2], tmpfds[2]; - int ret = pipe(fds); + ret = pipe(fds); ok(ret >= 0, "fdset: pipe() works"); ret = pipe(tmpfds); /* 3. Add fd to set. */ - ret = fdset_add(set, fds[0], OS_EV_READ); + ret = fdset_add(&set, fds[0], POLLIN, NULL); ok(ret == 0, "fdset: add to set works"); - fdset_add(set, tmpfds[0], OS_EV_READ); + fdset_add(&set, tmpfds[0], POLLIN, NULL); /* Schedule write. */ struct timeval ts, te; @@ -122,54 +121,44 @@ static int fdset_tests_run(int argc, char *argv[]) pthread_create(&t, 0, thr_action, &fds[1]); /* 4. Watch fdset. */ - ret = fdset_wait(set, OS_EV_FOREVER); + int nfds = poll(set.pfd, set.n, 2000); gettimeofday(&te, 0); size_t diff = timeval_diff(&ts, &te); - ok(ret > 0 && diff > 99 && diff < 10000, - "fdset: poll returned events in %zu ms", diff); + ok(nfds > 0 && diff > 99 && diff < 10000, + "fdset: poll returned %d events in %zu ms", nfds, diff); /* 5. Prepare event set. */ - fdset_it_t it; - ret = fdset_begin(set, &it); - ok(ret == 0 && it.fd == fds[0], "fdset: begin is valid, ret=%d", ret); + ok(set.pfd[0].revents & POLLIN, "fdset: pipe is active"); /* 6. Receive data. */ char buf = 0x00; - ret = read(it.fd, &buf, WRITE_PATTERN_LEN); - ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data, fd=%d", it.fd); - - /* 7. Iterate event set. */ - ret = fdset_next(set, &it); - ok(ret < 0, "fdset: boundary check works"); + ret = read(set.pfd[0].fd, &buf, WRITE_PATTERN_LEN); + ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data"); - /* 8. Remove from event set. */ - ret = fdset_remove(set, fds[0]); + /* 7-9. Remove from event set. */ + ret = fdset_remove(&set, 0); ok(ret == 0, "fdset: remove from fdset works"); close(fds[0]); close(fds[1]); - ret = fdset_remove(set, tmpfds[0]); + ret = fdset_remove(&set, 0); close(tmpfds[1]); close(tmpfds[1]); - - /* 9. Poll empty fdset. */ - ret = fdset_wait(set, OS_EV_FOREVER); - ok(ret <= 0, "fdset: polling empty fdset returns -1 (ret=%d)", ret); + ok(ret == 0, "fdset: remove from fdset works (2)"); + ret = fdset_remove(&set, 0); + ok(ret != 0, "fdset: removing nonexistent item"); /* 10. Crash test. */ lives_ok({ - fdset_destroy(0); - fdset_add(0, -1, 0); - fdset_remove(0, -1); - fdset_wait(0, OS_EV_NOWAIT); - fdset_begin(0, 0); - fdset_end(0, 0); - fdset_next(0, 0); - fdset_method(); + fdset_init(0, 0); + fdset_add(0, 1, 1, 0); + fdset_add(0, 0, 1, 0); + fdset_remove(0, 1); + fdset_remove(0, 0); }, "fdset: crash test successful"); /* 11. Destroy fdset. */ - ret = fdset_destroy(set); + ret = fdset_clear(&set); ok(ret == 0, "fdset: destroyed"); /* Cleanup. */