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