xref: /freebsd/contrib/ntp/libntp/recvbuff.c (revision 767173cec2b2041e1f847bc8896092f9c1481242)
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 
14*767173ceSCy Schubert #if (RECV_INC & (RECV_INC-1))
15*767173ceSCy Schubert # error RECV_INC not a power of 2!
16*767173ceSCy Schubert #endif
17*767173ceSCy Schubert #if (RECV_BATCH & (RECV_BATCH - 1))
18*767173ceSCy Schubert #error RECV_BATCH not a power of 2!
19*767173ceSCy Schubert #endif
20*767173ceSCy Schubert #if (RECV_BATCH < RECV_INC)
21*767173ceSCy Schubert #error RECV_BATCH must be >= RECV_INC!
22*767173ceSCy 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 */
33*767173ceSCy Schubert static u_long limit_recvbufs;		/* maximum total of receive buffers */
34*767173ceSCy 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;
47*767173ceSCy Schubert static CRITICAL_SECTION FreeLock;
48*767173ceSCy Schubert # define LOCK_R()	EnterCriticalSection(&RecvLock)
49*767173ceSCy Schubert # define UNLOCK_R()	LeaveCriticalSection(&RecvLock)
50*767173ceSCy Schubert # define LOCK_F()	EnterCriticalSection(&FreeLock)
51*767173ceSCy Schubert # define UNLOCK_F()	LeaveCriticalSection(&FreeLock)
52c0b746e5SOllivier Robert #else
53*767173ceSCy Schubert # define LOCK_R()	do {} while (FALSE)
54*767173ceSCy Schubert # define UNLOCK_R()	do {} while (FALSE)
55*767173ceSCy Schubert # define LOCK_F()	do {} while (FALSE)
56*767173ceSCy 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
95*767173ceSCy Schubert create_buffers(
96*767173ceSCy Schubert 	size_t		nbufs)
97c0b746e5SOllivier Robert {
98*767173ceSCy Schubert #   ifndef DEBUG
99*767173ceSCy Schubert 	static const u_int chunk = RECV_INC;
100*767173ceSCy Schubert #   else
101*767173ceSCy Schubert 	/* Allocate each buffer individually so they can be free()d
102*767173ceSCy Schubert 	 * during ntpd shutdown on DEBUG builds to keep them out of heap
103*767173ceSCy Schubert 	 * leak reports.
104*767173ceSCy Schubert 	 */
105*767173ceSCy Schubert 	static const u_int chunk = 1;
106*767173ceSCy Schubert #   endif
107*767173ceSCy Schubert 
108ea906c41SOllivier Robert 	register recvbuf_t *bufp;
109*767173ceSCy Schubert 	u_int i;
110*767173ceSCy Schubert 	size_t abuf;
111*767173ceSCy Schubert 
112*767173ceSCy Schubert 	if (limit_recvbufs <= total_recvbufs)
113*767173ceSCy Schubert 		return;
114c0b746e5SOllivier Robert 
115ea906c41SOllivier Robert 	abuf = nbufs + buffer_shortfall;
116ea906c41SOllivier Robert 	buffer_shortfall = 0;
117ea906c41SOllivier Robert 
118*767173ceSCy Schubert 	if (abuf < nbufs || abuf > RECV_BATCH)
119*767173ceSCy Schubert 		abuf = RECV_BATCH;	/* clamp on overflow */
120*767173ceSCy Schubert 	else
121*767173ceSCy Schubert 		abuf += (~abuf + 1) & (RECV_INC - 1);	/* round up */
122ea906c41SOllivier Robert 
123*767173ceSCy Schubert 	if (abuf > (limit_recvbufs - total_recvbufs))
124*767173ceSCy Schubert 		abuf = limit_recvbufs - total_recvbufs;
125*767173ceSCy Schubert 	abuf += (~abuf + 1) & (chunk - 1);		/* round up */
126*767173ceSCy Schubert 
127*767173ceSCy Schubert 	while (abuf) {
128*767173ceSCy Schubert 		bufp = calloc(chunk, sizeof(*bufp));
129*767173ceSCy Schubert 		if (!bufp) {
130*767173ceSCy Schubert 			limit_recvbufs = total_recvbufs;
131*767173ceSCy Schubert 			break;
132ea906c41SOllivier Robert 		}
133*767173ceSCy Schubert 		for (i = chunk; i; --i,++bufp) {
134*767173ceSCy Schubert 			LINK_SLIST(free_recv_list, bufp, link);
135*767173ceSCy Schubert 		}
136*767173ceSCy Schubert 		free_recvbufs += chunk;
137*767173ceSCy Schubert 		total_recvbufs += chunk;
138*767173ceSCy Schubert 		abuf -= chunk;
139*767173ceSCy Schubert 	}
140*767173ceSCy Schubert 	++lowater_adds;
141c0b746e5SOllivier Robert }
142c0b746e5SOllivier Robert 
143c0b746e5SOllivier Robert void
144c0b746e5SOllivier Robert init_recvbuff(int nbufs)
145c0b746e5SOllivier Robert {
146c0b746e5SOllivier Robert 
147c0b746e5SOllivier Robert 	/*
148c0b746e5SOllivier Robert 	 * Init buffer free list and stat counters
149c0b746e5SOllivier Robert 	 */
150ea906c41SOllivier Robert 	free_recvbufs = total_recvbufs = 0;
151c0b746e5SOllivier Robert 	full_recvbufs = lowater_adds = 0;
152c0b746e5SOllivier Robert 
153*767173ceSCy Schubert 	limit_recvbufs = RECV_TOOMANY;
154*767173ceSCy Schubert 	emerg_recvbufs = RECV_CLOCK;
155*767173ceSCy Schubert 
156ea906c41SOllivier Robert 	create_buffers(nbufs);
157ea906c41SOllivier Robert 
158ea906c41SOllivier Robert #   if defined(SYS_WINNT)
159ea906c41SOllivier Robert 	InitializeCriticalSection(&RecvLock);
160*767173ceSCy Schubert 	InitializeCriticalSection(&FreeLock);
161c0b746e5SOllivier Robert #   endif
162c0b746e5SOllivier Robert 
1632b15cb3dSCy Schubert #   ifdef DEBUG
1642b15cb3dSCy Schubert 	atexit(&uninit_recvbuff);
1652b15cb3dSCy Schubert #   endif
166c0b746e5SOllivier Robert }
167c0b746e5SOllivier Robert 
1682b15cb3dSCy Schubert 
1692b15cb3dSCy Schubert #ifdef DEBUG
1702b15cb3dSCy Schubert static void
1712b15cb3dSCy Schubert uninit_recvbuff(void)
1722b15cb3dSCy Schubert {
1732b15cb3dSCy Schubert 	recvbuf_t *rbunlinked;
1742b15cb3dSCy Schubert 
1752b15cb3dSCy Schubert 	for (;;) {
1762b15cb3dSCy Schubert 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
1772b15cb3dSCy Schubert 		if (rbunlinked == NULL)
1782b15cb3dSCy Schubert 			break;
1792b15cb3dSCy Schubert 		free(rbunlinked);
1802b15cb3dSCy Schubert 	}
1812b15cb3dSCy Schubert 
1822b15cb3dSCy Schubert 	for (;;) {
1832b15cb3dSCy Schubert 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
1842b15cb3dSCy Schubert 		if (rbunlinked == NULL)
1852b15cb3dSCy Schubert 			break;
1862b15cb3dSCy Schubert 		free(rbunlinked);
1872b15cb3dSCy Schubert 	}
188*767173ceSCy Schubert #   if defined(SYS_WINNT)
189*767173ceSCy Schubert 	DeleteCriticalSection(&FreeLock);
190*767173ceSCy Schubert 	DeleteCriticalSection(&RecvLock);
191*767173ceSCy Schubert #   endif
1922b15cb3dSCy Schubert }
1932b15cb3dSCy Schubert #endif	/* DEBUG */
1942b15cb3dSCy Schubert 
1952b15cb3dSCy Schubert 
196c0b746e5SOllivier Robert /*
197c0b746e5SOllivier Robert  * freerecvbuf - make a single recvbuf available for reuse
198c0b746e5SOllivier Robert  */
199c0b746e5SOllivier Robert void
200ea906c41SOllivier Robert freerecvbuf(recvbuf_t *rb)
201c0b746e5SOllivier Robert {
2024990d495SXin LI 	if (rb) {
203*767173ceSCy Schubert 		if (--rb->used != 0) {
204ea906c41SOllivier Robert 			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
205*767173ceSCy Schubert 			rb->used = 0;
206*767173ceSCy Schubert 		}
207*767173ceSCy Schubert 		LOCK_F();
2082b15cb3dSCy Schubert 		LINK_SLIST(free_recv_list, rb, link);
209*767173ceSCy Schubert 		++free_recvbufs;
210*767173ceSCy Schubert 		UNLOCK_F();
211c0b746e5SOllivier Robert 	}
2124990d495SXin LI }
213c0b746e5SOllivier Robert 
214c0b746e5SOllivier Robert 
215c0b746e5SOllivier Robert void
216ea906c41SOllivier Robert add_full_recv_buffer(recvbuf_t *rb)
217c0b746e5SOllivier Robert {
218ea906c41SOllivier Robert 	if (rb == NULL) {
219ea906c41SOllivier Robert 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
220ea906c41SOllivier Robert 		return;
221c0b746e5SOllivier Robert 	}
222*767173ceSCy Schubert 	LOCK_R();
2232b15cb3dSCy Schubert 	LINK_FIFO(full_recv_fifo, rb, link);
224*767173ceSCy Schubert 	++full_recvbufs;
225*767173ceSCy Schubert 	UNLOCK_R();
226c0b746e5SOllivier Robert }
227c0b746e5SOllivier Robert 
2282b15cb3dSCy Schubert 
229ea906c41SOllivier Robert recvbuf_t *
230*767173ceSCy Schubert get_free_recv_buffer(
231*767173ceSCy Schubert     int /*BOOL*/ urgent
232*767173ceSCy Schubert     )
233c0b746e5SOllivier Robert {
234*767173ceSCy Schubert 	recvbuf_t *buffer = NULL;
2352b15cb3dSCy Schubert 
236*767173ceSCy Schubert 	LOCK_F();
237*767173ceSCy Schubert 	if (free_recvbufs > (urgent ? emerg_recvbufs : 0)) {
2382b15cb3dSCy Schubert 		UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
239c0b746e5SOllivier Robert 	}
240*767173ceSCy Schubert 
241*767173ceSCy Schubert 	if (buffer != NULL) {
242*767173ceSCy Schubert 		if (free_recvbufs)
243*767173ceSCy Schubert 			--free_recvbufs;
244*767173ceSCy Schubert 		initialise_buffer(buffer);
245*767173ceSCy Schubert 		++buffer->used;
246*767173ceSCy Schubert 	} else {
247*767173ceSCy Schubert 		++buffer_shortfall;
248*767173ceSCy Schubert 	}
249*767173ceSCy Schubert 	UNLOCK_F();
2502b15cb3dSCy Schubert 
2512b15cb3dSCy Schubert 	return buffer;
252c0b746e5SOllivier Robert }
253c0b746e5SOllivier Robert 
2542b15cb3dSCy Schubert 
255ea906c41SOllivier Robert #ifdef HAVE_IO_COMPLETION_PORT
256ea906c41SOllivier Robert recvbuf_t *
257*767173ceSCy Schubert get_free_recv_buffer_alloc(
258*767173ceSCy Schubert     int /*BOOL*/ urgent
259*767173ceSCy Schubert     )
260c0b746e5SOllivier Robert {
261*767173ceSCy Schubert 	LOCK_F();
262*767173ceSCy Schubert 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
263ea906c41SOllivier Robert 		create_buffers(RECV_INC);
264*767173ceSCy Schubert 	UNLOCK_F();
265*767173ceSCy Schubert 	return get_free_recv_buffer(urgent);
266c0b746e5SOllivier Robert }
267ea906c41SOllivier Robert #endif
268c0b746e5SOllivier Robert 
2692b15cb3dSCy Schubert 
270ea906c41SOllivier Robert recvbuf_t *
271c0b746e5SOllivier Robert get_full_recv_buffer(void)
272c0b746e5SOllivier Robert {
273ea906c41SOllivier Robert 	recvbuf_t *	rbuf;
2742b15cb3dSCy Schubert 
275ea906c41SOllivier Robert 	/*
276*767173ceSCy Schubert 	 * make sure there are free buffers when we wander off to do
277*767173ceSCy Schubert 	 * lengthy packet processing with any buffer we grab from the
278*767173ceSCy Schubert 	 * full list.
279ea906c41SOllivier Robert 	 *
280*767173ceSCy Schubert 	 * fixes malloc() interrupted by SIGIO risk (Bug 889)
281ea906c41SOllivier Robert 	 */
282*767173ceSCy Schubert 	LOCK_F();
283*767173ceSCy Schubert 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
284ea906c41SOllivier Robert 		create_buffers(RECV_INC);
285*767173ceSCy Schubert 	UNLOCK_F();
286ea906c41SOllivier Robert 
287ea906c41SOllivier Robert 	/*
288ea906c41SOllivier Robert 	 * try to grab a full buffer
289ea906c41SOllivier Robert 	 */
290*767173ceSCy Schubert 	LOCK_R();
2912b15cb3dSCy Schubert 	UNLINK_FIFO(rbuf, full_recv_fifo, link);
292*767173ceSCy Schubert 	if (rbuf != NULL && full_recvbufs)
293*767173ceSCy Schubert 		--full_recvbufs;
294*767173ceSCy Schubert 	UNLOCK_R();
2952b15cb3dSCy Schubert 
2962b15cb3dSCy Schubert 	return rbuf;
297ea906c41SOllivier Robert }
298ea906c41SOllivier Robert 
2992b15cb3dSCy Schubert 
3002b15cb3dSCy Schubert /*
3012b15cb3dSCy Schubert  * purge_recv_buffers_for_fd() - purges any previously-received input
3022b15cb3dSCy Schubert  *				 from a given file descriptor.
3032b15cb3dSCy Schubert  */
3042b15cb3dSCy Schubert void
3052b15cb3dSCy Schubert purge_recv_buffers_for_fd(
3064990d495SXin LI 	int	fd
3072b15cb3dSCy Schubert 	)
3082b15cb3dSCy Schubert {
3092b15cb3dSCy Schubert 	recvbuf_t *rbufp;
3102b15cb3dSCy Schubert 	recvbuf_t *next;
3112b15cb3dSCy Schubert 	recvbuf_t *punlinked;
312*767173ceSCy Schubert 	recvbuf_t *freelist = NULL;
3132b15cb3dSCy Schubert 
314*767173ceSCy Schubert 	/* We want to hold only one lock at a time. So we do a scan on
315*767173ceSCy Schubert 	 * the full buffer queue, collecting items as we go, and when
316*767173ceSCy Schubert 	 * done we spool the the collected items to 'freerecvbuf()'.
317*767173ceSCy Schubert 	 */
318*767173ceSCy Schubert 	LOCK_R();
3192b15cb3dSCy Schubert 
3202b15cb3dSCy Schubert 	for (rbufp = HEAD_FIFO(full_recv_fifo);
3212b15cb3dSCy Schubert 	     rbufp != NULL;
322*767173ceSCy Schubert 	     rbufp = next)
323*767173ceSCy Schubert 	{
3242b15cb3dSCy Schubert 		next = rbufp->link;
3254990d495SXin LI #	    ifdef HAVE_IO_COMPLETION_PORT
3264990d495SXin LI 		if (rbufp->dstadr == NULL && rbufp->fd == fd)
3274990d495SXin LI #	    else
3284990d495SXin LI 		if (rbufp->fd == fd)
3294990d495SXin LI #	    endif
3304990d495SXin LI 		{
3312b15cb3dSCy Schubert 			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
3322b15cb3dSCy Schubert 					rbufp, link, recvbuf_t);
3332b15cb3dSCy Schubert 			INSIST(punlinked == rbufp);
334*767173ceSCy Schubert 			if (full_recvbufs)
335*767173ceSCy Schubert 				--full_recvbufs;
336*767173ceSCy Schubert 			rbufp->link = freelist;
337*767173ceSCy Schubert 			freelist = rbufp;
3382b15cb3dSCy Schubert 		}
3392b15cb3dSCy Schubert 	}
3402b15cb3dSCy Schubert 
341*767173ceSCy Schubert 	UNLOCK_R();
342*767173ceSCy Schubert 
343*767173ceSCy Schubert 	while (freelist) {
344*767173ceSCy Schubert 		next = freelist->link;
345*767173ceSCy Schubert 		freerecvbuf(freelist);
346*767173ceSCy Schubert 		freelist = next;
347*767173ceSCy Schubert 	}
3482b15cb3dSCy Schubert }
3492b15cb3dSCy Schubert 
3502b15cb3dSCy Schubert 
351ea906c41SOllivier Robert /*
352ea906c41SOllivier Robert  * Checks to see if there are buffers to process
353ea906c41SOllivier Robert  */
354ea906c41SOllivier Robert isc_boolean_t has_full_recv_buffer(void)
355ea906c41SOllivier Robert {
3562b15cb3dSCy Schubert 	if (HEAD_FIFO(full_recv_fifo) != NULL)
357ea906c41SOllivier Robert 		return (ISC_TRUE);
358ea906c41SOllivier Robert 	else
359ea906c41SOllivier Robert 		return (ISC_FALSE);
360c0b746e5SOllivier Robert }
3612b15cb3dSCy Schubert 
3622b15cb3dSCy Schubert 
3632b15cb3dSCy Schubert #ifdef NTP_DEBUG_LISTS_H
3642b15cb3dSCy Schubert void
3652b15cb3dSCy Schubert check_gen_fifo_consistency(void *fifo)
3662b15cb3dSCy Schubert {
3672b15cb3dSCy Schubert 	gen_fifo *pf;
3682b15cb3dSCy Schubert 	gen_node *pthis;
3692b15cb3dSCy Schubert 	gen_node **pptail;
3702b15cb3dSCy Schubert 
3712b15cb3dSCy Schubert 	pf = fifo;
3722b15cb3dSCy Schubert 	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
3732b15cb3dSCy Schubert 		(NULL != pf->phead && NULL != pf->pptail));
3742b15cb3dSCy Schubert 
3752b15cb3dSCy Schubert 	pptail = &pf->phead;
3762b15cb3dSCy Schubert 	for (pthis = pf->phead;
3772b15cb3dSCy Schubert 	     pthis != NULL;
3782b15cb3dSCy Schubert 	     pthis = pthis->link)
3792b15cb3dSCy Schubert 		if (NULL != pthis->link)
3802b15cb3dSCy Schubert 			pptail = &pthis->link;
3812b15cb3dSCy Schubert 
3822b15cb3dSCy Schubert 	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
3832b15cb3dSCy Schubert }
3842b15cb3dSCy Schubert #endif	/* NTP_DEBUG_LISTS_H */
385