154359Sroberto#ifdef HAVE_CONFIG_H
254359Sroberto# include <config.h>
354359Sroberto#endif
454359Sroberto
554359Sroberto#include <stdio.h>
6290001Sglebius
7290001Sglebius#include "ntp_assert.h"
882498Sroberto#include "ntp_syslog.h"
954359Sroberto#include "ntp_stdlib.h"
10290001Sglebius#include "ntp_lists.h"
1154359Sroberto#include "recvbuff.h"
1254359Sroberto#include "iosignal.h"
1354359Sroberto
14290001Sglebius
1554359Sroberto/*
1654359Sroberto * Memory allocation
1754359Sroberto */
18290001Sglebiusstatic u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
19290001Sglebiusstatic u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
20182007Srobertostatic u_long volatile total_recvbufs;	/* total recvbufs currently in use */
2154359Srobertostatic u_long volatile lowater_adds;	/* number of times we have added memory */
22182007Srobertostatic u_long volatile buffer_shortfall;/* number of missed free receive buffers
23290001Sglebius					   between replenishments */
2454359Sroberto
25290001Sglebiusstatic DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
26290001Sglebiusstatic recvbuf_t *		   free_recv_list;
2754359Sroberto
28182007Sroberto#if defined(SYS_WINNT)
29182007Sroberto
30182007Sroberto/*
31182007Sroberto * For Windows we need to set up a lock to manipulate the
32182007Sroberto * recv buffers to prevent corruption. We keep it lock for as
33182007Sroberto * short a time as possible
34182007Sroberto */
35182007Srobertostatic CRITICAL_SECTION RecvLock;
36182007Sroberto# define LOCK()		EnterCriticalSection(&RecvLock)
37182007Sroberto# define UNLOCK()	LeaveCriticalSection(&RecvLock)
3854359Sroberto#else
39290001Sglebius# define LOCK()		do {} while (FALSE)
40290001Sglebius# define UNLOCK()	do {} while (FALSE)
4154359Sroberto#endif
4254359Sroberto
43290001Sglebius#ifdef DEBUG
44290001Sglebiusstatic void uninit_recvbuff(void);
45290001Sglebius#endif
46290001Sglebius
47290001Sglebius
4854359Srobertou_long
4954359Srobertofree_recvbuffs (void)
5054359Sroberto{
5154359Sroberto	return free_recvbufs;
5254359Sroberto}
5354359Sroberto
5454359Srobertou_long
5554359Srobertofull_recvbuffs (void)
5654359Sroberto{
57182007Sroberto	return full_recvbufs;
5854359Sroberto}
5954359Sroberto
6054359Srobertou_long
6154359Srobertototal_recvbuffs (void)
6254359Sroberto{
63182007Sroberto	return total_recvbufs;
6454359Sroberto}
6554359Sroberto
6654359Srobertou_long
6754359Srobertolowater_additions(void)
6854359Sroberto{
6954359Sroberto	return lowater_adds;
7054359Sroberto}
7154359Sroberto
72290001Sglebiusstatic inline void
73182007Srobertoinitialise_buffer(recvbuf_t *buff)
7454359Sroberto{
75290001Sglebius	ZERO(*buff);
7654359Sroberto}
7754359Sroberto
7854359Srobertostatic void
79182007Srobertocreate_buffers(int nbufs)
8054359Sroberto{
81182007Sroberto	register recvbuf_t *bufp;
82182007Sroberto	int i, abuf;
83182007Sroberto
84182007Sroberto	abuf = nbufs + buffer_shortfall;
85182007Sroberto	buffer_shortfall = 0;
86182007Sroberto
87290001Sglebius#ifndef DEBUG
88290001Sglebius	bufp = emalloc_zero(abuf * sizeof(*bufp));
89290001Sglebius#endif
90182007Sroberto
91290001Sglebius	for (i = 0; i < abuf; i++) {
92290001Sglebius#ifdef DEBUG
93290001Sglebius		/*
94290001Sglebius		 * Allocate each buffer individually so they can be
95290001Sglebius		 * free()d during ntpd shutdown on DEBUG builds to
96290001Sglebius		 * keep them out of heap leak reports.
97290001Sglebius		 */
98290001Sglebius		bufp = emalloc_zero(sizeof(*bufp));
99290001Sglebius#endif
100290001Sglebius		LINK_SLIST(free_recv_list, bufp, link);
101182007Sroberto		bufp++;
102182007Sroberto		free_recvbufs++;
103182007Sroberto		total_recvbufs++;
10454359Sroberto	}
10554359Sroberto	lowater_adds++;
10654359Sroberto}
10754359Sroberto
10854359Srobertovoid
10954359Srobertoinit_recvbuff(int nbufs)
11054359Sroberto{
11154359Sroberto
11254359Sroberto	/*
11354359Sroberto	 * Init buffer free list and stat counters
11454359Sroberto	 */
115182007Sroberto	free_recvbufs = total_recvbufs = 0;
116182007Sroberto	full_recvbufs = lowater_adds = 0;
11754359Sroberto
118182007Sroberto	create_buffers(nbufs);
11954359Sroberto
120182007Sroberto#if defined(SYS_WINNT)
121182007Sroberto	InitializeCriticalSection(&RecvLock);
12254359Sroberto#endif
12354359Sroberto
124290001Sglebius#ifdef DEBUG
125290001Sglebius	atexit(&uninit_recvbuff);
126290001Sglebius#endif
12754359Sroberto}
12854359Sroberto
129290001Sglebius
130290001Sglebius#ifdef DEBUG
131290001Sglebiusstatic void
132290001Sglebiusuninit_recvbuff(void)
133290001Sglebius{
134290001Sglebius	recvbuf_t *rbunlinked;
135290001Sglebius
136290001Sglebius	for (;;) {
137290001Sglebius		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
138290001Sglebius		if (rbunlinked == NULL)
139290001Sglebius			break;
140290001Sglebius		free(rbunlinked);
141290001Sglebius	}
142290001Sglebius
143290001Sglebius	for (;;) {
144290001Sglebius		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
145290001Sglebius		if (rbunlinked == NULL)
146290001Sglebius			break;
147290001Sglebius		free(rbunlinked);
148290001Sglebius	}
149290001Sglebius}
150290001Sglebius#endif	/* DEBUG */
151290001Sglebius
152290001Sglebius
15354359Sroberto/*
154182007Sroberto * freerecvbuf - make a single recvbuf available for reuse
15554359Sroberto */
156182007Srobertovoid
157182007Srobertofreerecvbuf(recvbuf_t *rb)
15854359Sroberto{
159298770Sdelphij	if (rb) {
160298770Sdelphij		LOCK();
161298770Sdelphij		rb->used--;
162298770Sdelphij		if (rb->used != 0)
163298770Sdelphij			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
164298770Sdelphij		LINK_SLIST(free_recv_list, rb, link);
165298770Sdelphij		free_recvbufs++;
166298770Sdelphij		UNLOCK();
16754359Sroberto	}
16854359Sroberto}
16954359Sroberto
170182007Sroberto
17154359Srobertovoid
172182007Srobertoadd_full_recv_buffer(recvbuf_t *rb)
17354359Sroberto{
174182007Sroberto	if (rb == NULL) {
175182007Sroberto		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
176182007Sroberto		return;
177182007Sroberto	}
178182007Sroberto	LOCK();
179290001Sglebius	LINK_FIFO(full_recv_fifo, rb, link);
180182007Sroberto	full_recvbufs++;
181182007Sroberto	UNLOCK();
18254359Sroberto}
18354359Sroberto
184290001Sglebius
185182007Srobertorecvbuf_t *
186182007Srobertoget_free_recv_buffer(void)
18754359Sroberto{
188290001Sglebius	recvbuf_t *buffer;
189290001Sglebius
190182007Sroberto	LOCK();
191290001Sglebius	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
192290001Sglebius	if (buffer != NULL) {
193182007Sroberto		free_recvbufs--;
194182007Sroberto		initialise_buffer(buffer);
195290001Sglebius		buffer->used++;
196290001Sglebius	} else {
197182007Sroberto		buffer_shortfall++;
19854359Sroberto	}
199182007Sroberto	UNLOCK();
200290001Sglebius
201290001Sglebius	return buffer;
20254359Sroberto}
20354359Sroberto
204290001Sglebius
205182007Sroberto#ifdef HAVE_IO_COMPLETION_PORT
206182007Srobertorecvbuf_t *
207182007Srobertoget_free_recv_buffer_alloc(void)
20854359Sroberto{
209290001Sglebius	recvbuf_t *buffer;
210290001Sglebius
211290001Sglebius	buffer = get_free_recv_buffer();
212290001Sglebius	if (NULL == buffer) {
213182007Sroberto		create_buffers(RECV_INC);
214182007Sroberto		buffer = get_free_recv_buffer();
21554359Sroberto	}
216290001Sglebius	ENSURE(buffer != NULL);
217182007Sroberto	return (buffer);
21854359Sroberto}
219182007Sroberto#endif
22054359Sroberto
221290001Sglebius
222182007Srobertorecvbuf_t *
22354359Srobertoget_full_recv_buffer(void)
22454359Sroberto{
225290001Sglebius	recvbuf_t *	rbuf;
226290001Sglebius
227182007Sroberto	LOCK();
228182007Sroberto
229182007Sroberto#ifdef HAVE_SIGNALED_IO
230182007Sroberto	/*
231182007Sroberto	 * make sure there are free buffers when we
232290001Sglebius	 * wander off to do lengthy packet processing with
233182007Sroberto	 * any buffer we grab from the full list.
234182007Sroberto	 *
235182007Sroberto	 * fixes malloc() interrupted by SIGIO risk
236182007Sroberto	 * (Bug 889)
237182007Sroberto	 */
238290001Sglebius	if (NULL == free_recv_list || buffer_shortfall > 0) {
239182007Sroberto		/*
240182007Sroberto		 * try to get us some more buffers
241182007Sroberto		 */
242182007Sroberto		create_buffers(RECV_INC);
243182007Sroberto	}
244182007Sroberto#endif
245182007Sroberto
246182007Sroberto	/*
247182007Sroberto	 * try to grab a full buffer
248182007Sroberto	 */
249290001Sglebius	UNLINK_FIFO(rbuf, full_recv_fifo, link);
250182007Sroberto	if (rbuf != NULL)
251290001Sglebius		full_recvbufs--;
252290001Sglebius	UNLOCK();
253290001Sglebius
254290001Sglebius	return rbuf;
255290001Sglebius}
256290001Sglebius
257290001Sglebius
258290001Sglebius/*
259290001Sglebius * purge_recv_buffers_for_fd() - purges any previously-received input
260290001Sglebius *				 from a given file descriptor.
261290001Sglebius */
262290001Sglebiusvoid
263290001Sglebiuspurge_recv_buffers_for_fd(
264298770Sdelphij	int	fd
265290001Sglebius	)
266290001Sglebius{
267290001Sglebius	recvbuf_t *rbufp;
268290001Sglebius	recvbuf_t *next;
269290001Sglebius	recvbuf_t *punlinked;
270290001Sglebius
271290001Sglebius	LOCK();
272290001Sglebius
273290001Sglebius	for (rbufp = HEAD_FIFO(full_recv_fifo);
274290001Sglebius	     rbufp != NULL;
275290001Sglebius	     rbufp = next) {
276290001Sglebius		next = rbufp->link;
277298770Sdelphij#	    ifdef HAVE_IO_COMPLETION_PORT
278298770Sdelphij		if (rbufp->dstadr == NULL && rbufp->fd == fd)
279298770Sdelphij#	    else
280298770Sdelphij		if (rbufp->fd == fd)
281298770Sdelphij#	    endif
282298770Sdelphij		{
283290001Sglebius			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
284290001Sglebius					rbufp, link, recvbuf_t);
285290001Sglebius			INSIST(punlinked == rbufp);
286290001Sglebius			full_recvbufs--;
287290001Sglebius			freerecvbuf(rbufp);
288290001Sglebius		}
28954359Sroberto	}
290290001Sglebius
291182007Sroberto	UNLOCK();
29254359Sroberto}
293182007Sroberto
294290001Sglebius
295182007Sroberto/*
296182007Sroberto * Checks to see if there are buffers to process
297182007Sroberto */
298182007Srobertoisc_boolean_t has_full_recv_buffer(void)
299182007Sroberto{
300290001Sglebius	if (HEAD_FIFO(full_recv_fifo) != NULL)
301182007Sroberto		return (ISC_TRUE);
302182007Sroberto	else
303182007Sroberto		return (ISC_FALSE);
304182007Sroberto}
305290001Sglebius
306290001Sglebius
307290001Sglebius#ifdef NTP_DEBUG_LISTS_H
308290001Sglebiusvoid
309290001Sglebiuscheck_gen_fifo_consistency(void *fifo)
310290001Sglebius{
311290001Sglebius	gen_fifo *pf;
312290001Sglebius	gen_node *pthis;
313290001Sglebius	gen_node **pptail;
314290001Sglebius
315290001Sglebius	pf = fifo;
316290001Sglebius	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
317290001Sglebius		(NULL != pf->phead && NULL != pf->pptail));
318290001Sglebius
319290001Sglebius	pptail = &pf->phead;
320290001Sglebius	for (pthis = pf->phead;
321290001Sglebius	     pthis != NULL;
322290001Sglebius	     pthis = pthis->link)
323290001Sglebius		if (NULL != pthis->link)
324290001Sglebius			pptail = &pthis->link;
325290001Sglebius
326290001Sglebius	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
327290001Sglebius}
328290001Sglebius#endif	/* NTP_DEBUG_LISTS_H */
329