1/*	$NetBSD: recvbuff.c,v 1.1.1.1 2009/12/13 16:55:05 kardel Exp $	*/
2
3#ifdef HAVE_CONFIG_H
4# include <config.h>
5#endif
6
7#include <stdio.h>
8#include "ntp_machine.h"
9#include "ntp_assert.h"
10#include "ntp_fp.h"
11#include "ntp_syslog.h"
12#include "ntp_stdlib.h"
13#include "ntp_io.h"
14#include "ntp_lists.h"
15#include "recvbuff.h"
16#include "iosignal.h"
17
18
19
20#ifdef DEBUG
21static void uninit_recvbuff(void);
22#endif
23
24/*
25 * Memory allocation
26 */
27static u_long volatile full_recvbufs;	/* number of recvbufs on fulllist */
28static u_long volatile free_recvbufs;	/* number of recvbufs on freelist */
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 */
33
34static ISC_LIST(recvbuf_t)	full_recv_list;	/* Currently used recv buffers */
35static recvbuf_t *		free_recv_list;	/* Currently unused buffers */
36
37#if defined(SYS_WINNT)
38
39/*
40 * For Windows we need to set up a lock to manipulate the
41 * recv buffers to prevent corruption. We keep it lock for as
42 * short a time as possible
43 */
44static CRITICAL_SECTION RecvLock;
45# define LOCK()		EnterCriticalSection(&RecvLock)
46# define UNLOCK()	LeaveCriticalSection(&RecvLock)
47#else
48# define LOCK()
49# define UNLOCK()
50#endif
51
52u_long
53free_recvbuffs (void)
54{
55	return free_recvbufs;
56}
57
58u_long
59full_recvbuffs (void)
60{
61	return full_recvbufs;
62}
63
64u_long
65total_recvbuffs (void)
66{
67	return total_recvbufs;
68}
69
70u_long
71lowater_additions(void)
72{
73	return lowater_adds;
74}
75
76static inline void
77initialise_buffer(recvbuf_t *buff)
78{
79	memset(buff, 0, sizeof(*buff));
80}
81
82static void
83create_buffers(int nbufs)
84{
85	register recvbuf_t *bufp;
86	int i, abuf;
87
88	abuf = nbufs + buffer_shortfall;
89	buffer_shortfall = 0;
90
91#ifndef DEBUG
92	bufp = emalloc(abuf * sizeof(*bufp));
93#endif
94
95	for (i = 0; i < abuf; i++) {
96#ifdef DEBUG
97		/*
98		 * Allocate each buffer individually so they can be
99		 * free()d during ntpd shutdown on DEBUG builds to
100		 * keep them out of heap leak reports.
101		 */
102		bufp = emalloc(sizeof(*bufp));
103#endif
104		memset(bufp, 0, sizeof(*bufp));
105		LINK_SLIST(free_recv_list, bufp, link.next);
106		bufp++;
107		free_recvbufs++;
108		total_recvbufs++;
109	}
110	lowater_adds++;
111}
112
113void
114init_recvbuff(int nbufs)
115{
116
117	/*
118	 * Init buffer free list and stat counters
119	 */
120	ISC_LIST_INIT(full_recv_list);
121	free_recvbufs = total_recvbufs = 0;
122	full_recvbufs = lowater_adds = 0;
123
124	create_buffers(nbufs);
125
126#if defined(SYS_WINNT)
127	InitializeCriticalSection(&RecvLock);
128#endif
129
130#ifdef DEBUG
131	atexit(&uninit_recvbuff);
132#endif
133}
134
135
136#ifdef DEBUG
137static void
138uninit_recvbuff(void)
139{
140	recvbuf_t *rbunlinked;
141
142	while ((rbunlinked = ISC_LIST_HEAD(full_recv_list)) != NULL) {
143		ISC_LIST_DEQUEUE_TYPE(full_recv_list, rbunlinked, link, recvbuf_t);
144		free(rbunlinked);
145	}
146
147	do {
148		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link.next);
149		if (rbunlinked != NULL)
150			free(rbunlinked);
151	} while (rbunlinked != NULL);
152}
153#endif	/* DEBUG */
154
155
156/*
157 * freerecvbuf - make a single recvbuf available for reuse
158 */
159void
160freerecvbuf(recvbuf_t *rb)
161{
162	if (rb == NULL) {
163		msyslog(LOG_ERR, "freerecvbuff received NULL buffer");
164		return;
165	}
166
167	LOCK();
168	(rb->used)--;
169	if (rb->used != 0)
170		msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
171	LINK_SLIST(free_recv_list, rb, link.next);
172	free_recvbufs++;
173	UNLOCK();
174}
175
176
177void
178add_full_recv_buffer(recvbuf_t *rb)
179{
180	if (rb == NULL) {
181		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
182		return;
183	}
184	LOCK();
185	ISC_LINK_INIT(rb, link);
186	ISC_LIST_APPEND(full_recv_list, rb, link);
187	full_recvbufs++;
188	UNLOCK();
189}
190
191recvbuf_t *
192get_free_recv_buffer(void)
193{
194	recvbuf_t *buffer;
195
196	LOCK();
197	UNLINK_HEAD_SLIST(buffer, free_recv_list, link.next);
198	if (buffer != NULL) {
199		free_recvbufs--;
200		initialise_buffer(buffer);
201		(buffer->used)++;
202	} else
203		buffer_shortfall++;
204	UNLOCK();
205	return (buffer);
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	NTP_ENSURE(buffer != NULL);
220	return (buffer);
221}
222#endif
223
224recvbuf_t *
225get_full_recv_buffer(void)
226{
227	recvbuf_t *rbuf;
228	LOCK();
229
230#ifdef HAVE_SIGNALED_IO
231	/*
232	 * make sure there are free buffers when we
233	 * wander off to do lengthy packet processing with
234	 * any buffer we grab from the full list.
235	 *
236	 * fixes malloc() interrupted by SIGIO risk
237	 * (Bug 889)
238	 */
239	if (NULL == free_recv_list || buffer_shortfall > 0) {
240		/*
241		 * try to get us some more buffers
242		 */
243		create_buffers(RECV_INC);
244	}
245#endif
246
247	/*
248	 * try to grab a full buffer
249	 */
250	rbuf = ISC_LIST_HEAD(full_recv_list);
251	if (rbuf != NULL) {
252		ISC_LIST_DEQUEUE_TYPE(full_recv_list, rbuf, link, recvbuf_t);
253		--full_recvbufs;
254	} else
255		/*
256		 * Make sure we reset the full count to 0
257		 */
258		full_recvbufs = 0;
259	UNLOCK();
260	return (rbuf);
261}
262
263/*
264 * Checks to see if there are buffers to process
265 */
266isc_boolean_t has_full_recv_buffer(void)
267{
268	if (ISC_LIST_HEAD(full_recv_list) != NULL)
269		return (ISC_TRUE);
270	else
271		return (ISC_FALSE);
272}
273