recvbuff.c revision 316068
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 = eallocarray(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) {
160		LOCK();
161		rb->used--;
162		if (rb->used != 0)
163			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
164		LINK_SLIST(free_recv_list, rb, link);
165		free_recvbufs++;
166		UNLOCK();
167	}
168}
169
170
171void
172add_full_recv_buffer(recvbuf_t *rb)
173{
174	if (rb == NULL) {
175		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
176		return;
177	}
178	LOCK();
179	LINK_FIFO(full_recv_fifo, rb, link);
180	full_recvbufs++;
181	UNLOCK();
182}
183
184
185recvbuf_t *
186get_free_recv_buffer(void)
187{
188	recvbuf_t *buffer;
189
190	LOCK();
191	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
192	if (buffer != NULL) {
193		free_recvbufs--;
194		initialise_buffer(buffer);
195		buffer->used++;
196	} else {
197		buffer_shortfall++;
198	}
199	UNLOCK();
200
201	return buffer;
202}
203
204
205#ifdef HAVE_IO_COMPLETION_PORT
206recvbuf_t *
207get_free_recv_buffer_alloc(void)
208{
209	recvbuf_t *buffer;
210
211	buffer = get_free_recv_buffer();
212	if (NULL == buffer) {
213		create_buffers(RECV_INC);
214		buffer = get_free_recv_buffer();
215	}
216	ENSURE(buffer != NULL);
217	return (buffer);
218}
219#endif
220
221
222recvbuf_t *
223get_full_recv_buffer(void)
224{
225	recvbuf_t *	rbuf;
226
227	LOCK();
228
229#ifdef HAVE_SIGNALED_IO
230	/*
231	 * make sure there are free buffers when we
232	 * wander off to do lengthy packet processing with
233	 * any buffer we grab from the full list.
234	 *
235	 * fixes malloc() interrupted by SIGIO risk
236	 * (Bug 889)
237	 */
238	if (NULL == free_recv_list || buffer_shortfall > 0) {
239		/*
240		 * try to get us some more buffers
241		 */
242		create_buffers(RECV_INC);
243	}
244#endif
245
246	/*
247	 * try to grab a full buffer
248	 */
249	UNLINK_FIFO(rbuf, full_recv_fifo, link);
250	if (rbuf != NULL)
251		full_recvbufs--;
252	UNLOCK();
253
254	return rbuf;
255}
256
257
258/*
259 * purge_recv_buffers_for_fd() - purges any previously-received input
260 *				 from a given file descriptor.
261 */
262void
263purge_recv_buffers_for_fd(
264	int	fd
265	)
266{
267	recvbuf_t *rbufp;
268	recvbuf_t *next;
269	recvbuf_t *punlinked;
270
271	LOCK();
272
273	for (rbufp = HEAD_FIFO(full_recv_fifo);
274	     rbufp != NULL;
275	     rbufp = next) {
276		next = rbufp->link;
277#	    ifdef HAVE_IO_COMPLETION_PORT
278		if (rbufp->dstadr == NULL && rbufp->fd == fd)
279#	    else
280		if (rbufp->fd == fd)
281#	    endif
282		{
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