diff options
Diffstat (limited to 'contrib/ntp/libntp/recvbuff.c')
-rw-r--r-- | contrib/ntp/libntp/recvbuff.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/contrib/ntp/libntp/recvbuff.c b/contrib/ntp/libntp/recvbuff.c new file mode 100644 index 000000000000..6e7cda5d373d --- /dev/null +++ b/contrib/ntp/libntp/recvbuff.c @@ -0,0 +1,398 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#include "ntp_assert.h" +#include "ntp_syslog.h" +#include "ntp_stdlib.h" +#include "ntp_lists.h" +#include "recvbuff.h" +#include "iosignal.h" + +#if (RECV_INC & (RECV_INC-1)) +# error RECV_INC not a power of 2! +#endif +#if (RECV_BATCH & (RECV_BATCH - 1)) +#error RECV_BATCH not a power of 2! +#endif +#if (RECV_BATCH < RECV_INC) +#error RECV_BATCH must be >= RECV_INC! +#endif + +/* + * Memory allocation + */ +static u_long volatile full_recvbufs; /* recvbufs on full_recv_fifo */ +static u_long volatile free_recvbufs; /* recvbufs on free_recv_list */ +static u_long volatile total_recvbufs; /* total recvbufs currently in use */ +static u_long volatile lowater_adds; /* number of times we have added memory */ +static u_long volatile buffer_shortfall;/* number of missed free receive buffers + between replenishments */ +static u_long limit_recvbufs; /* maximum total of receive buffers */ +static u_long emerg_recvbufs; /* emergency/urgent buffers to keep */ + +static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo; +static recvbuf_t * free_recv_list; + +#if defined(SYS_WINNT) + +/* + * For Windows we need to set up a lock to manipulate the + * recv buffers to prevent corruption. We keep it lock for as + * short a time as possible + */ +static CRITICAL_SECTION RecvLock; +static CRITICAL_SECTION FreeLock; +# define LOCK_R() EnterCriticalSection(&RecvLock) +# define UNLOCK_R() LeaveCriticalSection(&RecvLock) +# define LOCK_F() EnterCriticalSection(&FreeLock) +# define UNLOCK_F() LeaveCriticalSection(&FreeLock) +#else +# define LOCK_R() do {} while (FALSE) +# define UNLOCK_R() do {} while (FALSE) +# define LOCK_F() do {} while (FALSE) +# define UNLOCK_F() do {} while (FALSE) +#endif + +#ifdef DEBUG +static void uninit_recvbuff(void); +#endif + + +u_long +free_recvbuffs (void) +{ + return free_recvbufs; +} + +u_long +full_recvbuffs (void) +{ + return full_recvbufs; +} + +u_long +total_recvbuffs (void) +{ + return total_recvbufs; +} + +u_long +lowater_additions(void) +{ + return lowater_adds; +} + +static inline void +initialise_buffer(recvbuf_t *buff) +{ + ZERO(*buff); +} + +static void +create_buffers( + size_t nbufs +) +{ + static const u_int chunk = +# ifndef DEBUG + RECV_INC; +# else + /* Allocate each buffer individually so they can be free()d + * during ntpd shutdown on DEBUG builds to keep them out of heap + * leak reports. + */ + 1; +# endif + static int/*BOOL*/ doneonce; + recvbuf_t * bufp; + u_int i; + size_t abuf; + + /*[bug 3666]: followup -- reset shortfalls in all cases */ + abuf = nbufs + buffer_shortfall; + buffer_shortfall = 0; + + if (limit_recvbufs <= total_recvbufs) { + if (!doneonce) { + msyslog(LOG_CRIT, "Unable to allocate receive" + " buffer, %lu/%lu", + total_recvbufs, limit_recvbufs); + doneonce = TRUE; + } + return; + } + + if (abuf < nbufs || abuf > RECV_BATCH) { + abuf = RECV_BATCH; /* clamp on overflow */ + } else { + abuf += (~abuf + 1) & (RECV_INC - 1); /* round up */ + } + if (abuf > (limit_recvbufs - total_recvbufs)) { + abuf = limit_recvbufs - total_recvbufs; + } + abuf += (~abuf + 1) & (chunk - 1); /* round up */ + + while (abuf) { + bufp = calloc(chunk, sizeof(*bufp)); + if (!bufp) { + msyslog(LOG_CRIT, "Out of memory, allocating " + "%u recvbufs, %lu bytes", + chunk, (u_long)sizeof(*bufp) * chunk); + limit_recvbufs = total_recvbufs; + break; + } + for (i = chunk; i; --i,++bufp) { + LINK_SLIST(free_recv_list, bufp, link); + } + free_recvbufs += chunk; + total_recvbufs += chunk; + abuf -= chunk; + } + ++lowater_adds; +} + +void +init_recvbuff(int nbufs) +{ + + /* + * Init buffer free list and stat counters + */ + free_recvbufs = total_recvbufs = 0; + full_recvbufs = lowater_adds = 0; + + limit_recvbufs = RECV_TOOMANY; + emerg_recvbufs = RECV_CLOCK; + + create_buffers(nbufs); + +# if defined(SYS_WINNT) + InitializeCriticalSection(&RecvLock); + InitializeCriticalSection(&FreeLock); +# endif + +# ifdef DEBUG + atexit(&uninit_recvbuff); +# endif +} + + +#ifdef DEBUG +static void +uninit_recvbuff(void) +{ + recvbuf_t *rbunlinked; + + for (;;) { + UNLINK_FIFO(rbunlinked, full_recv_fifo, link); + if (rbunlinked == NULL) + break; + free(rbunlinked); + } + + for (;;) { + UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link); + if (rbunlinked == NULL) + break; + free(rbunlinked); + } +# if defined(SYS_WINNT) + DeleteCriticalSection(&FreeLock); + DeleteCriticalSection(&RecvLock); +# endif +} +#endif /* DEBUG */ + + +/* + * freerecvbuf - make a single recvbuf available for reuse + */ +void +freerecvbuf(recvbuf_t *rb) +{ + if (rb) { + if (--rb->used != 0) { + msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used); + rb->used = 0; + } + LOCK_F(); + LINK_SLIST(free_recv_list, rb, link); + ++free_recvbufs; + UNLOCK_F(); + } +} + + +void +add_full_recv_buffer(recvbuf_t *rb) +{ + if (rb == NULL) { + msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer"); + return; + } + LOCK_R(); + LINK_FIFO(full_recv_fifo, rb, link); + ++full_recvbufs; + UNLOCK_R(); +} + + +recvbuf_t * +get_free_recv_buffer( + int /*BOOL*/ urgent + ) +{ + recvbuf_t *buffer = NULL; + + LOCK_F(); + if (free_recvbufs > (urgent ? 0 : emerg_recvbufs)) { + UNLINK_HEAD_SLIST(buffer, free_recv_list, link); + } + + if (buffer != NULL) { + if (free_recvbufs) + --free_recvbufs; + initialise_buffer(buffer); + ++buffer->used; + } else { + ++buffer_shortfall; + } + UNLOCK_F(); + + return buffer; +} + + +#ifdef HAVE_IO_COMPLETION_PORT +recvbuf_t * +get_free_recv_buffer_alloc( + int /*BOOL*/ urgent + ) +{ + LOCK_F(); + if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0) + create_buffers(RECV_INC); + UNLOCK_F(); + return get_free_recv_buffer(urgent); +} +#endif + + +recvbuf_t * +get_full_recv_buffer(void) +{ + recvbuf_t * rbuf; + + /* + * make sure there are free buffers when we wander off to do + * lengthy packet processing with any buffer we grab from the + * full list. + * + * fixes malloc() interrupted by SIGIO risk (Bug 889) + */ + LOCK_F(); + if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0) + create_buffers(RECV_INC); + UNLOCK_F(); + + /* + * try to grab a full buffer + */ + LOCK_R(); + UNLINK_FIFO(rbuf, full_recv_fifo, link); + if (rbuf != NULL && full_recvbufs) + --full_recvbufs; + UNLOCK_R(); + + return rbuf; +} + + +/* + * purge_recv_buffers_for_fd() - purges any previously-received input + * from a given file descriptor. + */ +void +purge_recv_buffers_for_fd( + int fd + ) +{ + recvbuf_t *rbufp; + recvbuf_t *next; + recvbuf_t *punlinked; + recvbuf_t *freelist = NULL; + + /* We want to hold only one lock at a time. So we do a scan on + * the full buffer queue, collecting items as we go, and when + * done we spool the the collected items to 'freerecvbuf()'. + */ + LOCK_R(); + + for (rbufp = HEAD_FIFO(full_recv_fifo); + rbufp != NULL; + rbufp = next) + { + next = rbufp->link; +# ifdef HAVE_IO_COMPLETION_PORT + if (rbufp->dstadr == NULL && rbufp->fd == fd) +# else + if (rbufp->fd == fd) +# endif + { + UNLINK_MID_FIFO(punlinked, full_recv_fifo, + rbufp, link, recvbuf_t); + INSIST(punlinked == rbufp); + if (full_recvbufs) + --full_recvbufs; + rbufp->link = freelist; + freelist = rbufp; + } + } + + UNLOCK_R(); + + while (freelist) { + next = freelist->link; + freerecvbuf(freelist); + freelist = next; + } +} + + +/* + * Checks to see if there are buffers to process + */ +isc_boolean_t has_full_recv_buffer(void) +{ + if (HEAD_FIFO(full_recv_fifo) != NULL) + return (ISC_TRUE); + else + return (ISC_FALSE); +} + + +#ifdef NTP_DEBUG_LISTS_H +void +check_gen_fifo_consistency(void *fifo) +{ + gen_fifo *pf; + gen_node *pthis; + gen_node **pptail; + + pf = fifo; + REQUIRE((NULL == pf->phead && NULL == pf->pptail) || + (NULL != pf->phead && NULL != pf->pptail)); + + pptail = &pf->phead; + for (pthis = pf->phead; + pthis != NULL; + pthis = pthis->link) + if (NULL != pthis->link) + pptail = &pthis->link; + + REQUIRE(NULL == pf->pptail || pptail == pf->pptail); +} +#endif /* NTP_DEBUG_LISTS_H */ |