recvbuff.c revision 1.7
1/*	$NetBSD: recvbuff.c,v 1.7 2017/04/13 20:17:42 christos Exp $	*/
2
3#ifdef HAVE_CONFIG_H
4# include <config.h>
5#endif
6
7#include <stdio.h>
8
9#include "ntp_assert.h"
10#include "ntp_syslog.h"
11#include "ntp_stdlib.h"
12#include "ntp_lists.h"
13#include "recvbuff.h"
14#include "iosignal.h"
15
16
17/*
18 * Memory allocation
19 */
20static u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
21static u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
22static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
23static u_long volatile lowater_adds;	/* number of times we have added memory */
24static u_long volatile buffer_shortfall;/* number of missed free receive buffers
25					   between replenishments */
26
27static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
28static recvbuf_t *		   free_recv_list;
29
30#if defined(SYS_WINNT)
31
32/*
33 * For Windows we need to set up a lock to manipulate the
34 * recv buffers to prevent corruption. We keep it lock for as
35 * short a time as possible
36 */
37static CRITICAL_SECTION RecvLock;
38# define LOCK()		EnterCriticalSection(&RecvLock)
39# define UNLOCK()	LeaveCriticalSection(&RecvLock)
40#else
41# define LOCK()		do {} while (FALSE)
42# define UNLOCK()	do {} while (FALSE)
43#endif
44
45#ifdef DEBUG
46static void uninit_recvbuff(void);
47#endif
48
49
50u_long
51free_recvbuffs (void)
52{
53	return free_recvbufs;
54}
55
56u_long
57full_recvbuffs (void)
58{
59	return full_recvbufs;
60}
61
62u_long
63total_recvbuffs (void)
64{
65	return total_recvbufs;
66}
67
68u_long
69lowater_additions(void)
70{
71	return lowater_adds;
72}
73
74static inline void
75initialise_buffer(recvbuf_t *buff)
76{
77	ZERO(*buff);
78}
79
80static void
81create_buffers(int nbufs)
82{
83	register recvbuf_t *bufp;
84	int i, abuf;
85
86	abuf = nbufs + buffer_shortfall;
87	buffer_shortfall = 0;
88
89#ifndef DEBUG
90	bufp = eallocarray(abuf, sizeof(*bufp));
91#endif
92
93	for (i = 0; i < abuf; i++) {
94#ifdef DEBUG
95		/*
96		 * Allocate each buffer individually so they can be
97		 * free()d during ntpd shutdown on DEBUG builds to
98		 * keep them out of heap leak reports.
99		 */
100		bufp = emalloc_zero(sizeof(*bufp));
101#endif
102		LINK_SLIST(free_recv_list, bufp, link);
103		bufp++;
104		free_recvbufs++;
105		total_recvbufs++;
106	}
107	lowater_adds++;
108}
109
110void
111init_recvbuff(int nbufs)
112{
113
114	/*
115	 * Init buffer free list and stat counters
116	 */
117	free_recvbufs = total_recvbufs = 0;
118	full_recvbufs = lowater_adds = 0;
119
120	create_buffers(nbufs);
121
122#if defined(SYS_WINNT)
123	InitializeCriticalSection(&RecvLock);
124#endif
125
126#ifdef DEBUG
127	atexit(&uninit_recvbuff);
128#endif
129}
130
131
132#ifdef DEBUG
133static void
134uninit_recvbuff(void)
135{
136	recvbuf_t *rbunlinked;
137
138	for (;;) {
139		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
140		if (rbunlinked == NULL)
141			break;
142		free(rbunlinked);
143	}
144
145	for (;;) {
146		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
147		if (rbunlinked == NULL)
148			break;
149		free(rbunlinked);
150	}
151}
152#endif	/* DEBUG */
153
154
155/*
156 * freerecvbuf - make a single recvbuf available for reuse
157 */
158void
159freerecvbuf(recvbuf_t *rb)
160{
161	if (rb) {
162		LOCK();
163		rb->used--;
164		if (rb->used != 0)
165			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
166		LINK_SLIST(free_recv_list, rb, link);
167		free_recvbufs++;
168		UNLOCK();
169	}
170}
171
172
173void
174add_full_recv_buffer(recvbuf_t *rb)
175{
176	if (rb == NULL) {
177		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
178		return;
179	}
180	LOCK();
181	LINK_FIFO(full_recv_fifo, rb, link);
182	full_recvbufs++;
183	UNLOCK();
184}
185
186
187recvbuf_t *
188get_free_recv_buffer(void)
189{
190	recvbuf_t *buffer;
191
192	LOCK();
193	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
194	if (buffer != NULL) {
195		free_recvbufs--;
196		initialise_buffer(buffer);
197		buffer->used++;
198	} else {
199		buffer_shortfall++;
200	}
201	UNLOCK();
202
203	return buffer;
204}
205
206
207#ifdef HAVE_IO_COMPLETION_PORT
208recvbuf_t *
209get_free_recv_buffer_alloc(void)
210{
211	recvbuf_t *buffer;
212
213	buffer = get_free_recv_buffer();
214	if (NULL == buffer) {
215		create_buffers(RECV_INC);
216		buffer = get_free_recv_buffer();
217	}
218	ENSURE(buffer != NULL);
219	return (buffer);
220}
221#endif
222
223
224recvbuf_t *
225get_full_recv_buffer(void)
226{
227	recvbuf_t *	rbuf;
228
229	LOCK();
230
231#ifdef HAVE_SIGNALED_IO
232	/*
233	 * make sure there are free buffers when we
234	 * wander off to do lengthy packet processing with
235	 * any buffer we grab from the full list.
236	 *
237	 * fixes malloc() interrupted by SIGIO risk
238	 * (Bug 889)
239	 */
240	if (NULL == free_recv_list || buffer_shortfall > 0) {
241		/*
242		 * try to get us some more buffers
243		 */
244		create_buffers(RECV_INC);
245	}
246#endif
247
248	/*
249	 * try to grab a full buffer
250	 */
251	UNLINK_FIFO(rbuf, full_recv_fifo, link);
252	if (rbuf != NULL)
253		full_recvbufs--;
254	UNLOCK();
255
256	return rbuf;
257}
258
259
260/*
261 * purge_recv_buffers_for_fd() - purges any previously-received input
262 *				 from a given file descriptor.
263 */
264void
265purge_recv_buffers_for_fd(
266	int	fd
267	)
268{
269	recvbuf_t *rbufp;
270	recvbuf_t *next;
271	recvbuf_t *punlinked;
272
273	LOCK();
274
275	for (rbufp = HEAD_FIFO(full_recv_fifo);
276	     rbufp != NULL;
277	     rbufp = next) {
278		next = rbufp->link;
279#	    ifdef HAVE_IO_COMPLETION_PORT
280		if (rbufp->dstadr == NULL && rbufp->fd == fd)
281#	    else
282		if (rbufp->fd == fd)
283#	    endif
284		{
285			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
286					rbufp, link, recvbuf_t);
287			INSIST(punlinked == rbufp);
288			full_recvbufs--;
289			freerecvbuf(rbufp);
290		}
291	}
292
293	UNLOCK();
294}
295
296
297/*
298 * Checks to see if there are buffers to process
299 */
300isc_boolean_t has_full_recv_buffer(void)
301{
302	if (HEAD_FIFO(full_recv_fifo) != NULL)
303		return (ISC_TRUE);
304	else
305		return (ISC_FALSE);
306}
307
308
309#ifdef NTP_DEBUG_LISTS_H
310void
311check_gen_fifo_consistency(void *fifo)
312{
313	gen_fifo *pf;
314	gen_node *pthis;
315	gen_node **pptail;
316
317	pf = fifo;
318	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
319		(NULL != pf->phead && NULL != pf->pptail));
320
321	pptail = &pf->phead;
322	for (pthis = pf->phead;
323	     pthis != NULL;
324	     pthis = pthis->link)
325		if (NULL != pthis->link)
326			pptail = &pthis->link;
327
328	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
329}
330#endif	/* NTP_DEBUG_LISTS_H */
331