1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdio.h>
6#include "ntp_machine.h"
7#include "ntp_assert.h"
8#include "ntp_fp.h"
9#include "ntp_syslog.h"
10#include "ntp_stdlib.h"
11#include "ntp_io.h"
12#include "ntp_lists.h"
13#include "recvbuff.h"
14#include "iosignal.h"
15
16
17
18#ifdef DEBUG
19static void uninit_recvbuff(void);
20#endif
21
22/*
23 * Memory allocation
24 */
25static u_long volatile full_recvbufs;	/* number of recvbufs on fulllist */
26static u_long volatile free_recvbufs;	/* number of recvbufs on freelist */
27static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
28static u_long volatile lowater_adds;	/* number of times we have added memory */
29static u_long volatile buffer_shortfall;/* number of missed free receive buffers
30					   between replenishments */
31
32static ISC_LIST(recvbuf_t)	full_recv_list;	/* Currently used recv buffers */
33static recvbuf_t *		free_recv_list;	/* Currently unused buffers */
34
35#if defined(SYS_WINNT)
36
37/*
38 * For Windows we need to set up a lock to manipulate the
39 * recv buffers to prevent corruption. We keep it lock for as
40 * short a time as possible
41 */
42static CRITICAL_SECTION RecvLock;
43# define LOCK()		EnterCriticalSection(&RecvLock)
44# define UNLOCK()	LeaveCriticalSection(&RecvLock)
45#else
46# define LOCK()
47# define UNLOCK()
48#endif
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	memset(buff, 0, sizeof(*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(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(sizeof(*bufp));
101#endif
102		memset(bufp, 0, sizeof(*bufp));
103		LINK_SLIST(free_recv_list, bufp, link.next);
104		bufp++;
105		free_recvbufs++;
106		total_recvbufs++;
107	}
108	lowater_adds++;
109}
110
111void
112init_recvbuff(int nbufs)
113{
114
115	/*
116	 * Init buffer free list and stat counters
117	 */
118	ISC_LIST_INIT(full_recv_list);
119	free_recvbufs = total_recvbufs = 0;
120	full_recvbufs = lowater_adds = 0;
121
122	create_buffers(nbufs);
123
124#if defined(SYS_WINNT)
125	InitializeCriticalSection(&RecvLock);
126#endif
127
128#ifdef DEBUG
129	atexit(&uninit_recvbuff);
130#endif
131}
132
133
134#ifdef DEBUG
135static void
136uninit_recvbuff(void)
137{
138	recvbuf_t *rbunlinked;
139
140	while ((rbunlinked = ISC_LIST_HEAD(full_recv_list)) != NULL) {
141		ISC_LIST_DEQUEUE_TYPE(full_recv_list, rbunlinked, link, recvbuf_t);
142		free(rbunlinked);
143	}
144
145	do {
146		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link.next);
147		if (rbunlinked != NULL)
148			free(rbunlinked);
149	} while (rbunlinked != NULL);
150}
151#endif	/* DEBUG */
152
153
154/*
155 * freerecvbuf - make a single recvbuf available for reuse
156 */
157void
158freerecvbuf(recvbuf_t *rb)
159{
160	if (rb == NULL) {
161		msyslog(LOG_ERR, "freerecvbuff received NULL buffer");
162		return;
163	}
164
165	LOCK();
166	(rb->used)--;
167	if (rb->used != 0)
168		msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
169	LINK_SLIST(free_recv_list, rb, link.next);
170	free_recvbufs++;
171	UNLOCK();
172}
173
174
175void
176add_full_recv_buffer(recvbuf_t *rb)
177{
178	if (rb == NULL) {
179		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
180		return;
181	}
182	LOCK();
183	ISC_LINK_INIT(rb, link);
184	ISC_LIST_APPEND(full_recv_list, rb, link);
185	full_recvbufs++;
186	UNLOCK();
187}
188
189recvbuf_t *
190get_free_recv_buffer(void)
191{
192	recvbuf_t *buffer;
193
194	LOCK();
195	UNLINK_HEAD_SLIST(buffer, free_recv_list, link.next);
196	if (buffer != NULL) {
197		free_recvbufs--;
198		initialise_buffer(buffer);
199		(buffer->used)++;
200	} else
201		buffer_shortfall++;
202	UNLOCK();
203	return (buffer);
204}
205
206#ifdef HAVE_IO_COMPLETION_PORT
207recvbuf_t *
208get_free_recv_buffer_alloc(void)
209{
210	recvbuf_t *buffer;
211
212	buffer = get_free_recv_buffer();
213	if (NULL == buffer) {
214		create_buffers(RECV_INC);
215		buffer = get_free_recv_buffer();
216	}
217	NTP_ENSURE(buffer != NULL);
218	return (buffer);
219}
220#endif
221
222recvbuf_t *
223get_full_recv_buffer(void)
224{
225	recvbuf_t *rbuf;
226	LOCK();
227
228#ifdef HAVE_SIGNALED_IO
229	/*
230	 * make sure there are free buffers when we
231	 * wander off to do lengthy packet processing with
232	 * any buffer we grab from the full list.
233	 *
234	 * fixes malloc() interrupted by SIGIO risk
235	 * (Bug 889)
236	 */
237	if (NULL == free_recv_list || buffer_shortfall > 0) {
238		/*
239		 * try to get us some more buffers
240		 */
241		create_buffers(RECV_INC);
242	}
243#endif
244
245	/*
246	 * try to grab a full buffer
247	 */
248	rbuf = ISC_LIST_HEAD(full_recv_list);
249	if (rbuf != NULL) {
250		ISC_LIST_DEQUEUE_TYPE(full_recv_list, rbuf, link, recvbuf_t);
251		--full_recvbufs;
252	} else
253		/*
254		 * Make sure we reset the full count to 0
255		 */
256		full_recvbufs = 0;
257	UNLOCK();
258	return (rbuf);
259}
260
261/*
262 * Checks to see if there are buffers to process
263 */
264isc_boolean_t has_full_recv_buffer(void)
265{
266	if (ISC_LIST_HEAD(full_recv_list) != NULL)
267		return (ISC_TRUE);
268	else
269		return (ISC_FALSE);
270}
271