aboutsummaryrefslogtreecommitdiff
path: root/contrib/ntp/libntp/recvbuff.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/libntp/recvbuff.c')
-rw-r--r--contrib/ntp/libntp/recvbuff.c398
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 */