1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6
7#include "ntp_assert.h"
8#include "ntp_syslog.h"
9#include "ntp_stdlib.h"
10#include "ntp_lists.h"
11#include "recvbuff.h"
12#include "iosignal.h"
13
14#if (RECV_INC & (RECV_INC-1))
15# error RECV_INC not a power of 2!
16#endif
17#if (RECV_BATCH & (RECV_BATCH - 1))
18#error RECV_BATCH not a power of 2!
19#endif
20#if (RECV_BATCH < RECV_INC)
21#error RECV_BATCH must be >= RECV_INC!
22#endif
23
24/*
25 * Memory allocation
26 */
27static u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
28static u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
29static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
30static u_long volatile lowater_adds;	/* number of times we have added memory */
31static u_long volatile buffer_shortfall;/* number of missed free receive buffers
32					   between replenishments */
33static u_long limit_recvbufs;		/* maximum total of receive buffers */
34static u_long emerg_recvbufs;		/* emergency/urgent buffers to keep */
35
36static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
37static recvbuf_t *		   free_recv_list;
38
39#if defined(SYS_WINNT)
40
41/*
42 * For Windows we need to set up a lock to manipulate the
43 * recv buffers to prevent corruption. We keep it lock for as
44 * short a time as possible
45 */
46static CRITICAL_SECTION RecvLock;
47static CRITICAL_SECTION FreeLock;
48# define LOCK_R()	EnterCriticalSection(&RecvLock)
49# define UNLOCK_R()	LeaveCriticalSection(&RecvLock)
50# define LOCK_F()	EnterCriticalSection(&FreeLock)
51# define UNLOCK_F()	LeaveCriticalSection(&FreeLock)
52#else
53# define LOCK_R()	do {} while (FALSE)
54# define UNLOCK_R()	do {} while (FALSE)
55# define LOCK_F()	do {} while (FALSE)
56# define UNLOCK_F()	do {} while (FALSE)
57#endif
58
59#ifdef DEBUG
60static void uninit_recvbuff(void);
61#endif
62
63
64u_long
65free_recvbuffs (void)
66{
67	return free_recvbufs;
68}
69
70u_long
71full_recvbuffs (void)
72{
73	return full_recvbufs;
74}
75
76u_long
77total_recvbuffs (void)
78{
79	return total_recvbufs;
80}
81
82u_long
83lowater_additions(void)
84{
85	return lowater_adds;
86}
87
88static inline void
89initialise_buffer(recvbuf_t *buff)
90{
91	ZERO(*buff);
92}
93
94static void
95create_buffers(
96	size_t			nbufs
97)
98{
99	static const u_int	chunk =
100#   ifndef DEBUG
101					RECV_INC;
102#   else
103	/* Allocate each buffer individually so they can be free()d
104	 * during ntpd shutdown on DEBUG builds to keep them out of heap
105	 * leak reports.
106	 */
107					1;
108#   endif
109	static int/*BOOL*/	doneonce;
110	recvbuf_t *		bufp;
111	u_int			i;
112	size_t			abuf;
113
114	/*[bug 3666]: followup -- reset shortfalls in all cases */
115	abuf = nbufs + buffer_shortfall;
116	buffer_shortfall = 0;
117
118	if (limit_recvbufs <= total_recvbufs) {
119		if (!doneonce) {
120			msyslog(LOG_CRIT, "Unable to allocate receive"
121					  " buffer, %lu/%lu",
122				total_recvbufs, limit_recvbufs);
123			doneonce = TRUE;
124		}
125		return;
126	}
127
128	if (abuf < nbufs || abuf > RECV_BATCH) {
129		abuf = RECV_BATCH;	/* clamp on overflow */
130	} else {
131		abuf += (~abuf + 1) & (RECV_INC - 1);	/* round up */
132	}
133	if (abuf > (limit_recvbufs - total_recvbufs)) {
134		abuf = limit_recvbufs - total_recvbufs;
135	}
136	abuf += (~abuf + 1) & (chunk - 1);		/* round up */
137
138	while (abuf) {
139		bufp = calloc(chunk, sizeof(*bufp));
140		if (!bufp) {
141			msyslog(LOG_CRIT, "Out of memory, allocating "
142					  "%u recvbufs, %lu bytes",
143				chunk, (u_long)sizeof(*bufp) * chunk);
144			limit_recvbufs = total_recvbufs;
145			break;
146		}
147		for (i = chunk; i; --i,++bufp) {
148			LINK_SLIST(free_recv_list, bufp, link);
149		}
150		free_recvbufs += chunk;
151		total_recvbufs += chunk;
152		abuf -= chunk;
153	}
154	++lowater_adds;
155}
156
157void
158init_recvbuff(int nbufs)
159{
160
161	/*
162	 * Init buffer free list and stat counters
163	 */
164	free_recvbufs = total_recvbufs = 0;
165	full_recvbufs = lowater_adds = 0;
166
167	limit_recvbufs = RECV_TOOMANY;
168	emerg_recvbufs = RECV_CLOCK;
169
170	create_buffers(nbufs);
171
172#   if defined(SYS_WINNT)
173	InitializeCriticalSection(&RecvLock);
174	InitializeCriticalSection(&FreeLock);
175#   endif
176
177#   ifdef DEBUG
178	atexit(&uninit_recvbuff);
179#   endif
180}
181
182
183#ifdef DEBUG
184static void
185uninit_recvbuff(void)
186{
187	recvbuf_t *rbunlinked;
188
189	for (;;) {
190		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
191		if (rbunlinked == NULL)
192			break;
193		free(rbunlinked);
194	}
195
196	for (;;) {
197		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
198		if (rbunlinked == NULL)
199			break;
200		free(rbunlinked);
201	}
202#   if defined(SYS_WINNT)
203	DeleteCriticalSection(&FreeLock);
204	DeleteCriticalSection(&RecvLock);
205#   endif
206}
207#endif	/* DEBUG */
208
209
210/*
211 * freerecvbuf - make a single recvbuf available for reuse
212 */
213void
214freerecvbuf(recvbuf_t *rb)
215{
216	if (rb) {
217		if (--rb->used != 0) {
218			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
219			rb->used = 0;
220		}
221		LOCK_F();
222		LINK_SLIST(free_recv_list, rb, link);
223		++free_recvbufs;
224		UNLOCK_F();
225	}
226}
227
228
229void
230add_full_recv_buffer(recvbuf_t *rb)
231{
232	if (rb == NULL) {
233		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
234		return;
235	}
236	LOCK_R();
237	LINK_FIFO(full_recv_fifo, rb, link);
238	++full_recvbufs;
239	UNLOCK_R();
240}
241
242
243recvbuf_t *
244get_free_recv_buffer(
245    int /*BOOL*/ urgent
246    )
247{
248	recvbuf_t *buffer = NULL;
249
250	LOCK_F();
251	if (free_recvbufs > (urgent ? 0 : emerg_recvbufs)) {
252		UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
253	}
254
255	if (buffer != NULL) {
256		if (free_recvbufs)
257			--free_recvbufs;
258		initialise_buffer(buffer);
259		++buffer->used;
260	} else {
261		++buffer_shortfall;
262	}
263	UNLOCK_F();
264
265	return buffer;
266}
267
268
269#ifdef HAVE_IO_COMPLETION_PORT
270recvbuf_t *
271get_free_recv_buffer_alloc(
272    int /*BOOL*/ urgent
273    )
274{
275	LOCK_F();
276	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
277		create_buffers(RECV_INC);
278	UNLOCK_F();
279	return get_free_recv_buffer(urgent);
280}
281#endif
282
283
284recvbuf_t *
285get_full_recv_buffer(void)
286{
287	recvbuf_t *	rbuf;
288
289	/*
290	 * make sure there are free buffers when we wander off to do
291	 * lengthy packet processing with any buffer we grab from the
292	 * full list.
293	 *
294	 * fixes malloc() interrupted by SIGIO risk (Bug 889)
295	 */
296	LOCK_F();
297	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
298		create_buffers(RECV_INC);
299	UNLOCK_F();
300
301	/*
302	 * try to grab a full buffer
303	 */
304	LOCK_R();
305	UNLINK_FIFO(rbuf, full_recv_fifo, link);
306	if (rbuf != NULL && full_recvbufs)
307		--full_recvbufs;
308	UNLOCK_R();
309
310	return rbuf;
311}
312
313
314/*
315 * purge_recv_buffers_for_fd() - purges any previously-received input
316 *				 from a given file descriptor.
317 */
318void
319purge_recv_buffers_for_fd(
320	int	fd
321	)
322{
323	recvbuf_t *rbufp;
324	recvbuf_t *next;
325	recvbuf_t *punlinked;
326	recvbuf_t *freelist = NULL;
327
328	/* We want to hold only one lock at a time. So we do a scan on
329	 * the full buffer queue, collecting items as we go, and when
330	 * done we spool the the collected items to 'freerecvbuf()'.
331	 */
332	LOCK_R();
333
334	for (rbufp = HEAD_FIFO(full_recv_fifo);
335	     rbufp != NULL;
336	     rbufp = next)
337	{
338		next = rbufp->link;
339#	    ifdef HAVE_IO_COMPLETION_PORT
340		if (rbufp->dstadr == NULL && rbufp->fd == fd)
341#	    else
342		if (rbufp->fd == fd)
343#	    endif
344		{
345			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
346					rbufp, link, recvbuf_t);
347			INSIST(punlinked == rbufp);
348			if (full_recvbufs)
349				--full_recvbufs;
350			rbufp->link = freelist;
351			freelist = rbufp;
352		}
353	}
354
355	UNLOCK_R();
356
357	while (freelist) {
358		next = freelist->link;
359		freerecvbuf(freelist);
360		freelist = next;
361	}
362}
363
364
365/*
366 * Checks to see if there are buffers to process
367 */
368isc_boolean_t has_full_recv_buffer(void)
369{
370	if (HEAD_FIFO(full_recv_fifo) != NULL)
371		return (ISC_TRUE);
372	else
373		return (ISC_FALSE);
374}
375
376
377#ifdef NTP_DEBUG_LISTS_H
378void
379check_gen_fifo_consistency(void *fifo)
380{
381	gen_fifo *pf;
382	gen_node *pthis;
383	gen_node **pptail;
384
385	pf = fifo;
386	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
387		(NULL != pf->phead && NULL != pf->pptail));
388
389	pptail = &pf->phead;
390	for (pthis = pf->phead;
391	     pthis != NULL;
392	     pthis = pthis->link)
393		if (NULL != pthis->link)
394			pptail = &pthis->link;
395
396	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
397}
398#endif	/* NTP_DEBUG_LISTS_H */
399