xref: /freebsd/contrib/ntp/libntp/recvbuff.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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(
96767173ceSCy Schubert 	size_t		nbufs)
97c0b746e5SOllivier Robert {
98767173ceSCy Schubert #   ifndef DEBUG
99767173ceSCy Schubert 	static const u_int chunk = RECV_INC;
100767173ceSCy Schubert #   else
101767173ceSCy Schubert 	/* Allocate each buffer individually so they can be free()d
102767173ceSCy Schubert 	 * during ntpd shutdown on DEBUG builds to keep them out of heap
103767173ceSCy Schubert 	 * leak reports.
104767173ceSCy Schubert 	 */
105767173ceSCy Schubert 	static const u_int chunk = 1;
106767173ceSCy Schubert #   endif
107767173ceSCy Schubert 
108ea906c41SOllivier Robert 	register recvbuf_t *bufp;
109767173ceSCy Schubert 	u_int i;
110767173ceSCy Schubert 	size_t abuf;
111767173ceSCy Schubert 
112*a466cc55SCy Schubert 	/*[bug 3666]: followup -- reset shortfalls in all cases */
113ea906c41SOllivier Robert 	abuf = nbufs + buffer_shortfall;
114ea906c41SOllivier Robert 	buffer_shortfall = 0;
115ea906c41SOllivier Robert 
116*a466cc55SCy Schubert 	if (limit_recvbufs <= total_recvbufs)
117*a466cc55SCy Schubert 		return;
118*a466cc55SCy Schubert 
119767173ceSCy Schubert 	if (abuf < nbufs || abuf > RECV_BATCH)
120767173ceSCy Schubert 		abuf = RECV_BATCH;	/* clamp on overflow */
121767173ceSCy Schubert 	else
122767173ceSCy Schubert 		abuf += (~abuf + 1) & (RECV_INC - 1);	/* round up */
123ea906c41SOllivier Robert 
124767173ceSCy Schubert 	if (abuf > (limit_recvbufs - total_recvbufs))
125767173ceSCy Schubert 		abuf = limit_recvbufs - total_recvbufs;
126767173ceSCy Schubert 	abuf += (~abuf + 1) & (chunk - 1);		/* round up */
127767173ceSCy Schubert 
128767173ceSCy Schubert 	while (abuf) {
129767173ceSCy Schubert 		bufp = calloc(chunk, sizeof(*bufp));
130767173ceSCy Schubert 		if (!bufp) {
131767173ceSCy Schubert 			limit_recvbufs = total_recvbufs;
132767173ceSCy Schubert 			break;
133ea906c41SOllivier Robert 		}
134767173ceSCy Schubert 		for (i = chunk; i; --i,++bufp) {
135767173ceSCy Schubert 			LINK_SLIST(free_recv_list, bufp, link);
136767173ceSCy Schubert 		}
137767173ceSCy Schubert 		free_recvbufs += chunk;
138767173ceSCy Schubert 		total_recvbufs += chunk;
139767173ceSCy Schubert 		abuf -= chunk;
140767173ceSCy Schubert 	}
141767173ceSCy Schubert 	++lowater_adds;
142c0b746e5SOllivier Robert }
143c0b746e5SOllivier Robert 
144c0b746e5SOllivier Robert void
145c0b746e5SOllivier Robert init_recvbuff(int nbufs)
146c0b746e5SOllivier Robert {
147c0b746e5SOllivier Robert 
148c0b746e5SOllivier Robert 	/*
149c0b746e5SOllivier Robert 	 * Init buffer free list and stat counters
150c0b746e5SOllivier Robert 	 */
151ea906c41SOllivier Robert 	free_recvbufs = total_recvbufs = 0;
152c0b746e5SOllivier Robert 	full_recvbufs = lowater_adds = 0;
153c0b746e5SOllivier Robert 
154767173ceSCy Schubert 	limit_recvbufs = RECV_TOOMANY;
155767173ceSCy Schubert 	emerg_recvbufs = RECV_CLOCK;
156767173ceSCy Schubert 
157ea906c41SOllivier Robert 	create_buffers(nbufs);
158ea906c41SOllivier Robert 
159ea906c41SOllivier Robert #   if defined(SYS_WINNT)
160ea906c41SOllivier Robert 	InitializeCriticalSection(&RecvLock);
161767173ceSCy Schubert 	InitializeCriticalSection(&FreeLock);
162c0b746e5SOllivier Robert #   endif
163c0b746e5SOllivier Robert 
1642b15cb3dSCy Schubert #   ifdef DEBUG
1652b15cb3dSCy Schubert 	atexit(&uninit_recvbuff);
1662b15cb3dSCy Schubert #   endif
167c0b746e5SOllivier Robert }
168c0b746e5SOllivier Robert 
1692b15cb3dSCy Schubert 
1702b15cb3dSCy Schubert #ifdef DEBUG
1712b15cb3dSCy Schubert static void
1722b15cb3dSCy Schubert uninit_recvbuff(void)
1732b15cb3dSCy Schubert {
1742b15cb3dSCy Schubert 	recvbuf_t *rbunlinked;
1752b15cb3dSCy Schubert 
1762b15cb3dSCy Schubert 	for (;;) {
1772b15cb3dSCy Schubert 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
1782b15cb3dSCy Schubert 		if (rbunlinked == NULL)
1792b15cb3dSCy Schubert 			break;
1802b15cb3dSCy Schubert 		free(rbunlinked);
1812b15cb3dSCy Schubert 	}
1822b15cb3dSCy Schubert 
1832b15cb3dSCy Schubert 	for (;;) {
1842b15cb3dSCy Schubert 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
1852b15cb3dSCy Schubert 		if (rbunlinked == NULL)
1862b15cb3dSCy Schubert 			break;
1872b15cb3dSCy Schubert 		free(rbunlinked);
1882b15cb3dSCy Schubert 	}
189767173ceSCy Schubert #   if defined(SYS_WINNT)
190767173ceSCy Schubert 	DeleteCriticalSection(&FreeLock);
191767173ceSCy Schubert 	DeleteCriticalSection(&RecvLock);
192767173ceSCy Schubert #   endif
1932b15cb3dSCy Schubert }
1942b15cb3dSCy Schubert #endif	/* DEBUG */
1952b15cb3dSCy Schubert 
1962b15cb3dSCy Schubert 
197c0b746e5SOllivier Robert /*
198c0b746e5SOllivier Robert  * freerecvbuf - make a single recvbuf available for reuse
199c0b746e5SOllivier Robert  */
200c0b746e5SOllivier Robert void
201ea906c41SOllivier Robert freerecvbuf(recvbuf_t *rb)
202c0b746e5SOllivier Robert {
2034990d495SXin LI 	if (rb) {
204767173ceSCy Schubert 		if (--rb->used != 0) {
205ea906c41SOllivier Robert 			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
206767173ceSCy Schubert 			rb->used = 0;
207767173ceSCy Schubert 		}
208767173ceSCy Schubert 		LOCK_F();
2092b15cb3dSCy Schubert 		LINK_SLIST(free_recv_list, rb, link);
210767173ceSCy Schubert 		++free_recvbufs;
211767173ceSCy Schubert 		UNLOCK_F();
212c0b746e5SOllivier Robert 	}
2134990d495SXin LI }
214c0b746e5SOllivier Robert 
215c0b746e5SOllivier Robert 
216c0b746e5SOllivier Robert void
217ea906c41SOllivier Robert add_full_recv_buffer(recvbuf_t *rb)
218c0b746e5SOllivier Robert {
219ea906c41SOllivier Robert 	if (rb == NULL) {
220ea906c41SOllivier Robert 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
221ea906c41SOllivier Robert 		return;
222c0b746e5SOllivier Robert 	}
223767173ceSCy Schubert 	LOCK_R();
2242b15cb3dSCy Schubert 	LINK_FIFO(full_recv_fifo, rb, link);
225767173ceSCy Schubert 	++full_recvbufs;
226767173ceSCy Schubert 	UNLOCK_R();
227c0b746e5SOllivier Robert }
228c0b746e5SOllivier Robert 
2292b15cb3dSCy Schubert 
230ea906c41SOllivier Robert recvbuf_t *
231767173ceSCy Schubert get_free_recv_buffer(
232767173ceSCy Schubert     int /*BOOL*/ urgent
233767173ceSCy Schubert     )
234c0b746e5SOllivier Robert {
235767173ceSCy Schubert 	recvbuf_t *buffer = NULL;
2362b15cb3dSCy Schubert 
237767173ceSCy Schubert 	LOCK_F();
238*a466cc55SCy Schubert 	if (free_recvbufs > (urgent ? 0 : emerg_recvbufs)) {
2392b15cb3dSCy Schubert 		UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
240c0b746e5SOllivier Robert 	}
241767173ceSCy Schubert 
242767173ceSCy Schubert 	if (buffer != NULL) {
243767173ceSCy Schubert 		if (free_recvbufs)
244767173ceSCy Schubert 			--free_recvbufs;
245767173ceSCy Schubert 		initialise_buffer(buffer);
246767173ceSCy Schubert 		++buffer->used;
247767173ceSCy Schubert 	} else {
248767173ceSCy Schubert 		++buffer_shortfall;
249767173ceSCy Schubert 	}
250767173ceSCy Schubert 	UNLOCK_F();
2512b15cb3dSCy Schubert 
2522b15cb3dSCy Schubert 	return buffer;
253c0b746e5SOllivier Robert }
254c0b746e5SOllivier Robert 
2552b15cb3dSCy Schubert 
256ea906c41SOllivier Robert #ifdef HAVE_IO_COMPLETION_PORT
257ea906c41SOllivier Robert recvbuf_t *
258767173ceSCy Schubert get_free_recv_buffer_alloc(
259767173ceSCy Schubert     int /*BOOL*/ urgent
260767173ceSCy Schubert     )
261c0b746e5SOllivier Robert {
262767173ceSCy Schubert 	LOCK_F();
263767173ceSCy Schubert 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
264ea906c41SOllivier Robert 		create_buffers(RECV_INC);
265767173ceSCy Schubert 	UNLOCK_F();
266767173ceSCy Schubert 	return get_free_recv_buffer(urgent);
267c0b746e5SOllivier Robert }
268ea906c41SOllivier Robert #endif
269c0b746e5SOllivier Robert 
2702b15cb3dSCy Schubert 
271ea906c41SOllivier Robert recvbuf_t *
272c0b746e5SOllivier Robert get_full_recv_buffer(void)
273c0b746e5SOllivier Robert {
274ea906c41SOllivier Robert 	recvbuf_t *	rbuf;
2752b15cb3dSCy Schubert 
276ea906c41SOllivier Robert 	/*
277767173ceSCy Schubert 	 * make sure there are free buffers when we wander off to do
278767173ceSCy Schubert 	 * lengthy packet processing with any buffer we grab from the
279767173ceSCy Schubert 	 * full list.
280ea906c41SOllivier Robert 	 *
281767173ceSCy Schubert 	 * fixes malloc() interrupted by SIGIO risk (Bug 889)
282ea906c41SOllivier Robert 	 */
283767173ceSCy Schubert 	LOCK_F();
284767173ceSCy Schubert 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
285ea906c41SOllivier Robert 		create_buffers(RECV_INC);
286767173ceSCy Schubert 	UNLOCK_F();
287ea906c41SOllivier Robert 
288ea906c41SOllivier Robert 	/*
289ea906c41SOllivier Robert 	 * try to grab a full buffer
290ea906c41SOllivier Robert 	 */
291767173ceSCy Schubert 	LOCK_R();
2922b15cb3dSCy Schubert 	UNLINK_FIFO(rbuf, full_recv_fifo, link);
293767173ceSCy Schubert 	if (rbuf != NULL && full_recvbufs)
294767173ceSCy Schubert 		--full_recvbufs;
295767173ceSCy Schubert 	UNLOCK_R();
2962b15cb3dSCy Schubert 
2972b15cb3dSCy Schubert 	return rbuf;
298ea906c41SOllivier Robert }
299ea906c41SOllivier Robert 
3002b15cb3dSCy Schubert 
3012b15cb3dSCy Schubert /*
3022b15cb3dSCy Schubert  * purge_recv_buffers_for_fd() - purges any previously-received input
3032b15cb3dSCy Schubert  *				 from a given file descriptor.
3042b15cb3dSCy Schubert  */
3052b15cb3dSCy Schubert void
3062b15cb3dSCy Schubert purge_recv_buffers_for_fd(
3074990d495SXin LI 	int	fd
3082b15cb3dSCy Schubert 	)
3092b15cb3dSCy Schubert {
3102b15cb3dSCy Schubert 	recvbuf_t *rbufp;
3112b15cb3dSCy Schubert 	recvbuf_t *next;
3122b15cb3dSCy Schubert 	recvbuf_t *punlinked;
313767173ceSCy Schubert 	recvbuf_t *freelist = NULL;
3142b15cb3dSCy Schubert 
315767173ceSCy Schubert 	/* We want to hold only one lock at a time. So we do a scan on
316767173ceSCy Schubert 	 * the full buffer queue, collecting items as we go, and when
317767173ceSCy Schubert 	 * done we spool the the collected items to 'freerecvbuf()'.
318767173ceSCy Schubert 	 */
319767173ceSCy Schubert 	LOCK_R();
3202b15cb3dSCy Schubert 
3212b15cb3dSCy Schubert 	for (rbufp = HEAD_FIFO(full_recv_fifo);
3222b15cb3dSCy Schubert 	     rbufp != NULL;
323767173ceSCy Schubert 	     rbufp = next)
324767173ceSCy Schubert 	{
3252b15cb3dSCy Schubert 		next = rbufp->link;
3264990d495SXin LI #	    ifdef HAVE_IO_COMPLETION_PORT
3274990d495SXin LI 		if (rbufp->dstadr == NULL && rbufp->fd == fd)
3284990d495SXin LI #	    else
3294990d495SXin LI 		if (rbufp->fd == fd)
3304990d495SXin LI #	    endif
3314990d495SXin LI 		{
3322b15cb3dSCy Schubert 			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
3332b15cb3dSCy Schubert 					rbufp, link, recvbuf_t);
3342b15cb3dSCy Schubert 			INSIST(punlinked == rbufp);
335767173ceSCy Schubert 			if (full_recvbufs)
336767173ceSCy Schubert 				--full_recvbufs;
337767173ceSCy Schubert 			rbufp->link = freelist;
338767173ceSCy Schubert 			freelist = rbufp;
3392b15cb3dSCy Schubert 		}
3402b15cb3dSCy Schubert 	}
3412b15cb3dSCy Schubert 
342767173ceSCy Schubert 	UNLOCK_R();
343767173ceSCy Schubert 
344767173ceSCy Schubert 	while (freelist) {
345767173ceSCy Schubert 		next = freelist->link;
346767173ceSCy Schubert 		freerecvbuf(freelist);
347767173ceSCy Schubert 		freelist = next;
348767173ceSCy Schubert 	}
3492b15cb3dSCy Schubert }
3502b15cb3dSCy Schubert 
3512b15cb3dSCy Schubert 
352ea906c41SOllivier Robert /*
353ea906c41SOllivier Robert  * Checks to see if there are buffers to process
354ea906c41SOllivier Robert  */
355ea906c41SOllivier Robert isc_boolean_t has_full_recv_buffer(void)
356ea906c41SOllivier Robert {
3572b15cb3dSCy Schubert 	if (HEAD_FIFO(full_recv_fifo) != NULL)
358ea906c41SOllivier Robert 		return (ISC_TRUE);
359ea906c41SOllivier Robert 	else
360ea906c41SOllivier Robert 		return (ISC_FALSE);
361c0b746e5SOllivier Robert }
3622b15cb3dSCy Schubert 
3632b15cb3dSCy Schubert 
3642b15cb3dSCy Schubert #ifdef NTP_DEBUG_LISTS_H
3652b15cb3dSCy Schubert void
3662b15cb3dSCy Schubert check_gen_fifo_consistency(void *fifo)
3672b15cb3dSCy Schubert {
3682b15cb3dSCy Schubert 	gen_fifo *pf;
3692b15cb3dSCy Schubert 	gen_node *pthis;
3702b15cb3dSCy Schubert 	gen_node **pptail;
3712b15cb3dSCy Schubert 
3722b15cb3dSCy Schubert 	pf = fifo;
3732b15cb3dSCy Schubert 	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
3742b15cb3dSCy Schubert 		(NULL != pf->phead && NULL != pf->pptail));
3752b15cb3dSCy Schubert 
3762b15cb3dSCy Schubert 	pptail = &pf->phead;
3772b15cb3dSCy Schubert 	for (pthis = pf->phead;
3782b15cb3dSCy Schubert 	     pthis != NULL;
3792b15cb3dSCy Schubert 	     pthis = pthis->link)
3802b15cb3dSCy Schubert 		if (NULL != pthis->link)
3812b15cb3dSCy Schubert 			pptail = &pthis->link;
3822b15cb3dSCy Schubert 
3832b15cb3dSCy Schubert 	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
3842b15cb3dSCy Schubert }
3852b15cb3dSCy Schubert #endif	/* NTP_DEBUG_LISTS_H */
386