recvbuff.c revision 1.4
1/*	$NetBSD: recvbuff.c,v 1.4 2015/10/23 18:06:19 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 = emalloc_zero(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 == NULL) {
162		msyslog(LOG_ERR, "freerecvbuff received NULL buffer");
163		return;
164	}
165
166	LOCK();
167	rb->used--;
168	if (rb->used != 0)
169		msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
170	LINK_SLIST(free_recv_list, rb, link);
171	free_recvbufs++;
172	UNLOCK();
173}
174
175
176void
177add_full_recv_buffer(recvbuf_t *rb)
178{
179	if (rb == NULL) {
180		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
181		return;
182	}
183	LOCK();
184	LINK_FIFO(full_recv_fifo, rb, link);
185	full_recvbufs++;
186	UNLOCK();
187}
188
189
190recvbuf_t *
191get_free_recv_buffer(void)
192{
193	recvbuf_t *buffer;
194
195	LOCK();
196	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
197	if (buffer != NULL) {
198		free_recvbufs--;
199		initialise_buffer(buffer);
200		buffer->used++;
201	} else {
202		buffer_shortfall++;
203	}
204	UNLOCK();
205
206	return buffer;
207}
208
209
210#ifdef HAVE_IO_COMPLETION_PORT
211recvbuf_t *
212get_free_recv_buffer_alloc(void)
213{
214	recvbuf_t *buffer;
215
216	buffer = get_free_recv_buffer();
217	if (NULL == buffer) {
218		create_buffers(RECV_INC);
219		buffer = get_free_recv_buffer();
220	}
221	ENSURE(buffer != NULL);
222	return (buffer);
223}
224#endif
225
226
227recvbuf_t *
228get_full_recv_buffer(void)
229{
230	recvbuf_t *	rbuf;
231
232	LOCK();
233
234#ifdef HAVE_SIGNALED_IO
235	/*
236	 * make sure there are free buffers when we
237	 * wander off to do lengthy packet processing with
238	 * any buffer we grab from the full list.
239	 *
240	 * fixes malloc() interrupted by SIGIO risk
241	 * (Bug 889)
242	 */
243	if (NULL == free_recv_list || buffer_shortfall > 0) {
244		/*
245		 * try to get us some more buffers
246		 */
247		create_buffers(RECV_INC);
248	}
249#endif
250
251	/*
252	 * try to grab a full buffer
253	 */
254	UNLINK_FIFO(rbuf, full_recv_fifo, link);
255	if (rbuf != NULL)
256		full_recvbufs--;
257	UNLOCK();
258
259	return rbuf;
260}
261
262
263/*
264 * purge_recv_buffers_for_fd() - purges any previously-received input
265 *				 from a given file descriptor.
266 */
267void
268purge_recv_buffers_for_fd(
269	SOCKET	fd
270	)
271{
272	recvbuf_t *rbufp;
273	recvbuf_t *next;
274	recvbuf_t *punlinked;
275
276	LOCK();
277
278	for (rbufp = HEAD_FIFO(full_recv_fifo);
279	     rbufp != NULL;
280	     rbufp = next) {
281		next = rbufp->link;
282		if (rbufp->fd == fd) {
283			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
284					rbufp, link, recvbuf_t);
285			INSIST(punlinked == rbufp);
286			full_recvbufs--;
287			freerecvbuf(rbufp);
288		}
289	}
290
291	UNLOCK();
292}
293
294
295/*
296 * Checks to see if there are buffers to process
297 */
298isc_boolean_t has_full_recv_buffer(void)
299{
300	if (HEAD_FIFO(full_recv_fifo) != NULL)
301		return (ISC_TRUE);
302	else
303		return (ISC_FALSE);
304}
305
306
307#ifdef NTP_DEBUG_LISTS_H
308void
309check_gen_fifo_consistency(void *fifo)
310{
311	gen_fifo *pf;
312	gen_node *pthis;
313	gen_node **pptail;
314
315	pf = fifo;
316	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
317		(NULL != pf->phead && NULL != pf->pptail));
318
319	pptail = &pf->phead;
320	for (pthis = pf->phead;
321	     pthis != NULL;
322	     pthis = pthis->link)
323		if (NULL != pthis->link)
324			pptail = &pthis->link;
325
326	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
327}
328#endif	/* NTP_DEBUG_LISTS_H */
329