recvbuff.c revision 358659
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	/*
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
244	/*
245	 * try to grab a full buffer
246	 */
247	UNLINK_FIFO(rbuf, full_recv_fifo, link);
248	if (rbuf != NULL)
249		full_recvbufs--;
250	UNLOCK();
251
252	return rbuf;
253}
254
255
256/*
257 * purge_recv_buffers_for_fd() - purges any previously-received input
258 *				 from a given file descriptor.
259 */
260void
261purge_recv_buffers_for_fd(
262	int	fd
263	)
264{
265	recvbuf_t *rbufp;
266	recvbuf_t *next;
267	recvbuf_t *punlinked;
268
269	LOCK();
270
271	for (rbufp = HEAD_FIFO(full_recv_fifo);
272	     rbufp != NULL;
273	     rbufp = next) {
274		next = rbufp->link;
275#	    ifdef HAVE_IO_COMPLETION_PORT
276		if (rbufp->dstadr == NULL && rbufp->fd == fd)
277#	    else
278		if (rbufp->fd == fd)
279#	    endif
280		{
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