xref: /freebsd/contrib/ntp/libntp/recvbuff.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
2c0b746e5SOllivier Robert # include <config.h>
3c0b746e5SOllivier Robert #endif
4c0b746e5SOllivier Robert 
5c0b746e5SOllivier Robert #include <stdio.h>
62b15cb3dSCy Schubert 
72b15cb3dSCy Schubert #include "ntp_assert.h"
8c0b746e5SOllivier Robert #include "ntp_syslog.h"
9224ba2bdSOllivier Robert #include "ntp_stdlib.h"
102b15cb3dSCy Schubert #include "ntp_lists.h"
11c0b746e5SOllivier Robert #include "recvbuff.h"
12c0b746e5SOllivier Robert #include "iosignal.h"
13c0b746e5SOllivier Robert 
14767173ceSCy Schubert #if (RECV_INC & (RECV_INC-1))
15767173ceSCy Schubert # error RECV_INC not a power of 2!
16767173ceSCy Schubert #endif
17767173ceSCy Schubert #if (RECV_BATCH & (RECV_BATCH - 1))
18767173ceSCy Schubert #error RECV_BATCH not a power of 2!
19767173ceSCy Schubert #endif
20767173ceSCy Schubert #if (RECV_BATCH < RECV_INC)
21767173ceSCy Schubert #error RECV_BATCH must be >= RECV_INC!
22767173ceSCy Schubert #endif
232b15cb3dSCy Schubert 
24c0b746e5SOllivier Robert /*
25c0b746e5SOllivier Robert  * Memory allocation
26c0b746e5SOllivier Robert  */
272b15cb3dSCy Schubert static u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
282b15cb3dSCy Schubert static u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
29c0b746e5SOllivier Robert static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
30c0b746e5SOllivier Robert static u_long volatile lowater_adds;	/* number of times we have added memory */
31ea906c41SOllivier Robert static u_long volatile buffer_shortfall;/* number of missed free receive buffers
32ea906c41SOllivier Robert 					   between replenishments */
33767173ceSCy Schubert static u_long limit_recvbufs;		/* maximum total of receive buffers */
34767173ceSCy Schubert static u_long emerg_recvbufs;		/* emergency/urgent buffers to keep */
35c0b746e5SOllivier Robert 
362b15cb3dSCy Schubert static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
372b15cb3dSCy Schubert static recvbuf_t *		   free_recv_list;
38c0b746e5SOllivier Robert 
39ea906c41SOllivier Robert #if defined(SYS_WINNT)
40ea906c41SOllivier Robert 
41ea906c41SOllivier Robert /*
42ea906c41SOllivier Robert  * For Windows we need to set up a lock to manipulate the
43ea906c41SOllivier Robert  * recv buffers to prevent corruption. We keep it lock for as
44ea906c41SOllivier Robert  * short a time as possible
45ea906c41SOllivier Robert  */
46ea906c41SOllivier Robert static CRITICAL_SECTION RecvLock;
47767173ceSCy Schubert static CRITICAL_SECTION FreeLock;
48767173ceSCy Schubert # define LOCK_R()	EnterCriticalSection(&RecvLock)
49767173ceSCy Schubert # define UNLOCK_R()	LeaveCriticalSection(&RecvLock)
50767173ceSCy Schubert # define LOCK_F()	EnterCriticalSection(&FreeLock)
51767173ceSCy Schubert # define UNLOCK_F()	LeaveCriticalSection(&FreeLock)
52c0b746e5SOllivier Robert #else
53767173ceSCy Schubert # define LOCK_R()	do {} while (FALSE)
54767173ceSCy Schubert # define UNLOCK_R()	do {} while (FALSE)
55767173ceSCy Schubert # define LOCK_F()	do {} while (FALSE)
56767173ceSCy Schubert # define UNLOCK_F()	do {} while (FALSE)
57c0b746e5SOllivier Robert #endif
58c0b746e5SOllivier Robert 
592b15cb3dSCy Schubert #ifdef DEBUG
602b15cb3dSCy Schubert static void uninit_recvbuff(void);
612b15cb3dSCy Schubert #endif
622b15cb3dSCy Schubert 
632b15cb3dSCy Schubert 
64c0b746e5SOllivier Robert u_long
65c0b746e5SOllivier Robert free_recvbuffs (void)
66c0b746e5SOllivier Robert {
67c0b746e5SOllivier Robert 	return free_recvbufs;
68c0b746e5SOllivier Robert }
69c0b746e5SOllivier Robert 
70c0b746e5SOllivier Robert u_long
71c0b746e5SOllivier Robert full_recvbuffs (void)
72c0b746e5SOllivier Robert {
73ea906c41SOllivier Robert 	return full_recvbufs;
74c0b746e5SOllivier Robert }
75c0b746e5SOllivier Robert 
76c0b746e5SOllivier Robert u_long
77c0b746e5SOllivier Robert total_recvbuffs (void)
78c0b746e5SOllivier Robert {
79ea906c41SOllivier Robert 	return total_recvbufs;
80c0b746e5SOllivier Robert }
81c0b746e5SOllivier Robert 
82c0b746e5SOllivier Robert u_long
83c0b746e5SOllivier Robert lowater_additions(void)
84c0b746e5SOllivier Robert {
85c0b746e5SOllivier Robert 	return lowater_adds;
86c0b746e5SOllivier Robert }
87c0b746e5SOllivier Robert 
882b15cb3dSCy Schubert static inline void
89ea906c41SOllivier Robert initialise_buffer(recvbuf_t *buff)
90c0b746e5SOllivier Robert {
912b15cb3dSCy Schubert 	ZERO(*buff);
92c0b746e5SOllivier Robert }
93c0b746e5SOllivier Robert 
94c0b746e5SOllivier Robert static void
95767173ceSCy Schubert create_buffers(
96*f5f40dd6SCy Schubert 	size_t			nbufs
97*f5f40dd6SCy Schubert )
98c0b746e5SOllivier Robert {
99*f5f40dd6SCy Schubert 	static const u_int	chunk =
100767173ceSCy Schubert #   ifndef DEBUG
101*f5f40dd6SCy Schubert 					RECV_INC;
102767173ceSCy Schubert #   else
103767173ceSCy Schubert 	/* Allocate each buffer individually so they can be free()d
104767173ceSCy Schubert 	 * during ntpd shutdown on DEBUG builds to keep them out of heap
105767173ceSCy Schubert 	 * leak reports.
106767173ceSCy Schubert 	 */
107*f5f40dd6SCy Schubert 					1;
108767173ceSCy Schubert #   endif
109*f5f40dd6SCy Schubert 	static int/*BOOL*/	doneonce;
110*f5f40dd6SCy Schubert 	recvbuf_t *		bufp;
111767173ceSCy Schubert 	u_int			i;
112767173ceSCy Schubert 	size_t			abuf;
113767173ceSCy Schubert 
114a466cc55SCy Schubert 	/*[bug 3666]: followup -- reset shortfalls in all cases */
115ea906c41SOllivier Robert 	abuf = nbufs + buffer_shortfall;
116ea906c41SOllivier Robert 	buffer_shortfall = 0;
117ea906c41SOllivier Robert 
118*f5f40dd6SCy Schubert 	if (limit_recvbufs <= total_recvbufs) {
119*f5f40dd6SCy Schubert 		if (!doneonce) {
120*f5f40dd6SCy Schubert 			msyslog(LOG_CRIT, "Unable to allocate receive"
121*f5f40dd6SCy Schubert 					  " buffer, %lu/%lu",
122*f5f40dd6SCy Schubert 				total_recvbufs, limit_recvbufs);
123*f5f40dd6SCy Schubert 			doneonce = TRUE;
124*f5f40dd6SCy Schubert 		}
125a466cc55SCy Schubert 		return;
126*f5f40dd6SCy Schubert 	}
127a466cc55SCy Schubert 
128*f5f40dd6SCy Schubert 	if (abuf < nbufs || abuf > RECV_BATCH) {
129767173ceSCy Schubert 		abuf = RECV_BATCH;	/* clamp on overflow */
130*f5f40dd6SCy Schubert 	} else {
131767173ceSCy Schubert 		abuf += (~abuf + 1) & (RECV_INC - 1);	/* round up */
132*f5f40dd6SCy Schubert 	}
133*f5f40dd6SCy Schubert 	if (abuf > (limit_recvbufs - total_recvbufs)) {
134767173ceSCy Schubert 		abuf = limit_recvbufs - total_recvbufs;
135*f5f40dd6SCy Schubert 	}
136767173ceSCy Schubert 	abuf += (~abuf + 1) & (chunk - 1);		/* round up */
137767173ceSCy Schubert 
138767173ceSCy Schubert 	while (abuf) {
139767173ceSCy Schubert 		bufp = calloc(chunk, sizeof(*bufp));
140767173ceSCy Schubert 		if (!bufp) {
141*f5f40dd6SCy Schubert 			msyslog(LOG_CRIT, "Out of memory, allocating "
142*f5f40dd6SCy Schubert 					  "%u recvbufs, %lu bytes",
143*f5f40dd6SCy Schubert 				chunk, (u_long)sizeof(*bufp) * chunk);
144767173ceSCy Schubert 			limit_recvbufs = total_recvbufs;
145767173ceSCy Schubert 			break;
146ea906c41SOllivier Robert 		}
147767173ceSCy Schubert 		for (i = chunk; i; --i,++bufp) {
148767173ceSCy Schubert 			LINK_SLIST(free_recv_list, bufp, link);
149767173ceSCy Schubert 		}
150767173ceSCy Schubert 		free_recvbufs += chunk;
151767173ceSCy Schubert 		total_recvbufs += chunk;
152767173ceSCy Schubert 		abuf -= chunk;
153767173ceSCy Schubert 	}
154767173ceSCy Schubert 	++lowater_adds;
155c0b746e5SOllivier Robert }
156c0b746e5SOllivier Robert 
157c0b746e5SOllivier Robert void
158c0b746e5SOllivier Robert init_recvbuff(int nbufs)
159c0b746e5SOllivier Robert {
160c0b746e5SOllivier Robert 
161c0b746e5SOllivier Robert 	/*
162c0b746e5SOllivier Robert 	 * Init buffer free list and stat counters
163c0b746e5SOllivier Robert 	 */
164ea906c41SOllivier Robert 	free_recvbufs = total_recvbufs = 0;
165c0b746e5SOllivier Robert 	full_recvbufs = lowater_adds = 0;
166c0b746e5SOllivier Robert 
167767173ceSCy Schubert 	limit_recvbufs = RECV_TOOMANY;
168767173ceSCy Schubert 	emerg_recvbufs = RECV_CLOCK;
169767173ceSCy Schubert 
170ea906c41SOllivier Robert 	create_buffers(nbufs);
171ea906c41SOllivier Robert 
172ea906c41SOllivier Robert #   if defined(SYS_WINNT)
173ea906c41SOllivier Robert 	InitializeCriticalSection(&RecvLock);
174767173ceSCy Schubert 	InitializeCriticalSection(&FreeLock);
175c0b746e5SOllivier Robert #   endif
176c0b746e5SOllivier Robert 
1772b15cb3dSCy Schubert #   ifdef DEBUG
1782b15cb3dSCy Schubert 	atexit(&uninit_recvbuff);
1792b15cb3dSCy Schubert #   endif
180c0b746e5SOllivier Robert }
181c0b746e5SOllivier Robert 
1822b15cb3dSCy Schubert 
1832b15cb3dSCy Schubert #ifdef DEBUG
1842b15cb3dSCy Schubert static void
1852b15cb3dSCy Schubert uninit_recvbuff(void)
1862b15cb3dSCy Schubert {
1872b15cb3dSCy Schubert 	recvbuf_t *rbunlinked;
1882b15cb3dSCy Schubert 
1892b15cb3dSCy Schubert 	for (;;) {
1902b15cb3dSCy Schubert 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
1912b15cb3dSCy Schubert 		if (rbunlinked == NULL)
1922b15cb3dSCy Schubert 			break;
1932b15cb3dSCy Schubert 		free(rbunlinked);
1942b15cb3dSCy Schubert 	}
1952b15cb3dSCy Schubert 
1962b15cb3dSCy Schubert 	for (;;) {
1972b15cb3dSCy Schubert 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
1982b15cb3dSCy Schubert 		if (rbunlinked == NULL)
1992b15cb3dSCy Schubert 			break;
2002b15cb3dSCy Schubert 		free(rbunlinked);
2012b15cb3dSCy Schubert 	}
202767173ceSCy Schubert #   if defined(SYS_WINNT)
203767173ceSCy Schubert 	DeleteCriticalSection(&FreeLock);
204767173ceSCy Schubert 	DeleteCriticalSection(&RecvLock);
205767173ceSCy Schubert #   endif
2062b15cb3dSCy Schubert }
2072b15cb3dSCy Schubert #endif	/* DEBUG */
2082b15cb3dSCy Schubert 
2092b15cb3dSCy Schubert 
210c0b746e5SOllivier Robert /*
211c0b746e5SOllivier Robert  * freerecvbuf - make a single recvbuf available for reuse
212c0b746e5SOllivier Robert  */
213c0b746e5SOllivier Robert void
214ea906c41SOllivier Robert freerecvbuf(recvbuf_t *rb)
215c0b746e5SOllivier Robert {
2164990d495SXin LI 	if (rb) {
217767173ceSCy Schubert 		if (--rb->used != 0) {
218ea906c41SOllivier Robert 			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
219767173ceSCy Schubert 			rb->used = 0;
220767173ceSCy Schubert 		}
221767173ceSCy Schubert 		LOCK_F();
2222b15cb3dSCy Schubert 		LINK_SLIST(free_recv_list, rb, link);
223767173ceSCy Schubert 		++free_recvbufs;
224767173ceSCy Schubert 		UNLOCK_F();
225c0b746e5SOllivier Robert 	}
2264990d495SXin LI }
227c0b746e5SOllivier Robert 
228c0b746e5SOllivier Robert 
229c0b746e5SOllivier Robert void
230ea906c41SOllivier Robert add_full_recv_buffer(recvbuf_t *rb)
231c0b746e5SOllivier Robert {
232ea906c41SOllivier Robert 	if (rb == NULL) {
233ea906c41SOllivier Robert 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
234ea906c41SOllivier Robert 		return;
235c0b746e5SOllivier Robert 	}
236767173ceSCy Schubert 	LOCK_R();
2372b15cb3dSCy Schubert 	LINK_FIFO(full_recv_fifo, rb, link);
238767173ceSCy Schubert 	++full_recvbufs;
239767173ceSCy Schubert 	UNLOCK_R();
240c0b746e5SOllivier Robert }
241c0b746e5SOllivier Robert 
2422b15cb3dSCy Schubert 
243ea906c41SOllivier Robert recvbuf_t *
244767173ceSCy Schubert get_free_recv_buffer(
245767173ceSCy Schubert     int /*BOOL*/ urgent
246767173ceSCy Schubert     )
247c0b746e5SOllivier Robert {
248767173ceSCy Schubert 	recvbuf_t *buffer = NULL;
2492b15cb3dSCy Schubert 
250767173ceSCy Schubert 	LOCK_F();
251a466cc55SCy Schubert 	if (free_recvbufs > (urgent ? 0 : emerg_recvbufs)) {
2522b15cb3dSCy Schubert 		UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
253c0b746e5SOllivier Robert 	}
254767173ceSCy Schubert 
255767173ceSCy Schubert 	if (buffer != NULL) {
256767173ceSCy Schubert 		if (free_recvbufs)
257767173ceSCy Schubert 			--free_recvbufs;
258767173ceSCy Schubert 		initialise_buffer(buffer);
259767173ceSCy Schubert 		++buffer->used;
260767173ceSCy Schubert 	} else {
261767173ceSCy Schubert 		++buffer_shortfall;
262767173ceSCy Schubert 	}
263767173ceSCy Schubert 	UNLOCK_F();
2642b15cb3dSCy Schubert 
2652b15cb3dSCy Schubert 	return buffer;
266c0b746e5SOllivier Robert }
267c0b746e5SOllivier Robert 
2682b15cb3dSCy Schubert 
269ea906c41SOllivier Robert #ifdef HAVE_IO_COMPLETION_PORT
270ea906c41SOllivier Robert recvbuf_t *
271767173ceSCy Schubert get_free_recv_buffer_alloc(
272767173ceSCy Schubert     int /*BOOL*/ urgent
273767173ceSCy Schubert     )
274c0b746e5SOllivier Robert {
275767173ceSCy Schubert 	LOCK_F();
276767173ceSCy Schubert 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
277ea906c41SOllivier Robert 		create_buffers(RECV_INC);
278767173ceSCy Schubert 	UNLOCK_F();
279767173ceSCy Schubert 	return get_free_recv_buffer(urgent);
280c0b746e5SOllivier Robert }
281ea906c41SOllivier Robert #endif
282c0b746e5SOllivier Robert 
2832b15cb3dSCy Schubert 
284ea906c41SOllivier Robert recvbuf_t *
285c0b746e5SOllivier Robert get_full_recv_buffer(void)
286c0b746e5SOllivier Robert {
287ea906c41SOllivier Robert 	recvbuf_t *	rbuf;
2882b15cb3dSCy Schubert 
289ea906c41SOllivier Robert 	/*
290767173ceSCy Schubert 	 * make sure there are free buffers when we wander off to do
291767173ceSCy Schubert 	 * lengthy packet processing with any buffer we grab from the
292767173ceSCy Schubert 	 * full list.
293ea906c41SOllivier Robert 	 *
294767173ceSCy Schubert 	 * fixes malloc() interrupted by SIGIO risk (Bug 889)
295ea906c41SOllivier Robert 	 */
296767173ceSCy Schubert 	LOCK_F();
297767173ceSCy Schubert 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
298ea906c41SOllivier Robert 		create_buffers(RECV_INC);
299767173ceSCy Schubert 	UNLOCK_F();
300ea906c41SOllivier Robert 
301ea906c41SOllivier Robert 	/*
302ea906c41SOllivier Robert 	 * try to grab a full buffer
303ea906c41SOllivier Robert 	 */
304767173ceSCy Schubert 	LOCK_R();
3052b15cb3dSCy Schubert 	UNLINK_FIFO(rbuf, full_recv_fifo, link);
306767173ceSCy Schubert 	if (rbuf != NULL && full_recvbufs)
307767173ceSCy Schubert 		--full_recvbufs;
308767173ceSCy Schubert 	UNLOCK_R();
3092b15cb3dSCy Schubert 
3102b15cb3dSCy Schubert 	return rbuf;
311ea906c41SOllivier Robert }
312ea906c41SOllivier Robert 
3132b15cb3dSCy Schubert 
3142b15cb3dSCy Schubert /*
3152b15cb3dSCy Schubert  * purge_recv_buffers_for_fd() - purges any previously-received input
3162b15cb3dSCy Schubert  *				 from a given file descriptor.
3172b15cb3dSCy Schubert  */
3182b15cb3dSCy Schubert void
3192b15cb3dSCy Schubert purge_recv_buffers_for_fd(
3204990d495SXin LI 	int	fd
3212b15cb3dSCy Schubert 	)
3222b15cb3dSCy Schubert {
3232b15cb3dSCy Schubert 	recvbuf_t *rbufp;
3242b15cb3dSCy Schubert 	recvbuf_t *next;
3252b15cb3dSCy Schubert 	recvbuf_t *punlinked;
326767173ceSCy Schubert 	recvbuf_t *freelist = NULL;
3272b15cb3dSCy Schubert 
328767173ceSCy Schubert 	/* We want to hold only one lock at a time. So we do a scan on
329767173ceSCy Schubert 	 * the full buffer queue, collecting items as we go, and when
330767173ceSCy Schubert 	 * done we spool the the collected items to 'freerecvbuf()'.
331767173ceSCy Schubert 	 */
332767173ceSCy Schubert 	LOCK_R();
3332b15cb3dSCy Schubert 
3342b15cb3dSCy Schubert 	for (rbufp = HEAD_FIFO(full_recv_fifo);
3352b15cb3dSCy Schubert 	     rbufp != NULL;
336767173ceSCy Schubert 	     rbufp = next)
337767173ceSCy Schubert 	{
3382b15cb3dSCy Schubert 		next = rbufp->link;
3394990d495SXin LI #	    ifdef HAVE_IO_COMPLETION_PORT
3404990d495SXin LI 		if (rbufp->dstadr == NULL && rbufp->fd == fd)
3414990d495SXin LI #	    else
3424990d495SXin LI 		if (rbufp->fd == fd)
3434990d495SXin LI #	    endif
3444990d495SXin LI 		{
3452b15cb3dSCy Schubert 			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
3462b15cb3dSCy Schubert 					rbufp, link, recvbuf_t);
3472b15cb3dSCy Schubert 			INSIST(punlinked == rbufp);
348767173ceSCy Schubert 			if (full_recvbufs)
349767173ceSCy Schubert 				--full_recvbufs;
350767173ceSCy Schubert 			rbufp->link = freelist;
351767173ceSCy Schubert 			freelist = rbufp;
3522b15cb3dSCy Schubert 		}
3532b15cb3dSCy Schubert 	}
3542b15cb3dSCy Schubert 
355767173ceSCy Schubert 	UNLOCK_R();
356767173ceSCy Schubert 
357767173ceSCy Schubert 	while (freelist) {
358767173ceSCy Schubert 		next = freelist->link;
359767173ceSCy Schubert 		freerecvbuf(freelist);
360767173ceSCy Schubert 		freelist = next;
361767173ceSCy Schubert 	}
3622b15cb3dSCy Schubert }
3632b15cb3dSCy Schubert 
3642b15cb3dSCy Schubert 
365ea906c41SOllivier Robert /*
366ea906c41SOllivier Robert  * Checks to see if there are buffers to process
367ea906c41SOllivier Robert  */
368ea906c41SOllivier Robert isc_boolean_t has_full_recv_buffer(void)
369ea906c41SOllivier Robert {
3702b15cb3dSCy Schubert 	if (HEAD_FIFO(full_recv_fifo) != NULL)
371ea906c41SOllivier Robert 		return (ISC_TRUE);
372ea906c41SOllivier Robert 	else
373ea906c41SOllivier Robert 		return (ISC_FALSE);
374c0b746e5SOllivier Robert }
3752b15cb3dSCy Schubert 
3762b15cb3dSCy Schubert 
3772b15cb3dSCy Schubert #ifdef NTP_DEBUG_LISTS_H
3782b15cb3dSCy Schubert void
3792b15cb3dSCy Schubert check_gen_fifo_consistency(void *fifo)
3802b15cb3dSCy Schubert {
3812b15cb3dSCy Schubert 	gen_fifo *pf;
3822b15cb3dSCy Schubert 	gen_node *pthis;
3832b15cb3dSCy Schubert 	gen_node **pptail;
3842b15cb3dSCy Schubert 
3852b15cb3dSCy Schubert 	pf = fifo;
3862b15cb3dSCy Schubert 	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
3872b15cb3dSCy Schubert 		(NULL != pf->phead && NULL != pf->pptail));
3882b15cb3dSCy Schubert 
3892b15cb3dSCy Schubert 	pptail = &pf->phead;
3902b15cb3dSCy Schubert 	for (pthis = pf->phead;
3912b15cb3dSCy Schubert 	     pthis != NULL;
3922b15cb3dSCy Schubert 	     pthis = pthis->link)
3932b15cb3dSCy Schubert 		if (NULL != pthis->link)
3942b15cb3dSCy Schubert 			pptail = &pthis->link;
3952b15cb3dSCy Schubert 
3962b15cb3dSCy Schubert 	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
3972b15cb3dSCy Schubert }
3982b15cb3dSCy Schubert #endif	/* NTP_DEBUG_LISTS_H */
399