xref: /netbsd-src/external/bsd/ntp/dist/libntp/recvbuff.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: recvbuff.c,v 1.1.1.2 2013/12/27 23:30:47 christos Exp $	*/
2 
3 #ifdef HAVE_CONFIG_H
4 # include <config.h>
5 #endif
6 
7 #include <stdio.h>
8 
9 #include "ntp_assert.h"
10 #include "ntp_syslog.h"
11 #include "ntp_stdlib.h"
12 #include "ntp_lists.h"
13 #include "recvbuff.h"
14 #include "iosignal.h"
15 
16 
17 /*
18  * Memory allocation
19  */
20 static u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
21 static u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
22 static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
23 static u_long volatile lowater_adds;	/* number of times we have added memory */
24 static u_long volatile buffer_shortfall;/* number of missed free receive buffers
25 					   between replenishments */
26 
27 static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
28 static recvbuf_t *		   free_recv_list;
29 
30 #if defined(SYS_WINNT)
31 
32 /*
33  * For Windows we need to set up a lock to manipulate the
34  * recv buffers to prevent corruption. We keep it lock for as
35  * short a time as possible
36  */
37 static CRITICAL_SECTION RecvLock;
38 # define LOCK()		EnterCriticalSection(&RecvLock)
39 # define UNLOCK()	LeaveCriticalSection(&RecvLock)
40 #else
41 # define LOCK()		do {} while (FALSE)
42 # define UNLOCK()	do {} while (FALSE)
43 #endif
44 
45 #ifdef DEBUG
46 static void uninit_recvbuff(void);
47 #endif
48 
49 
50 u_long
51 free_recvbuffs (void)
52 {
53 	return free_recvbufs;
54 }
55 
56 u_long
57 full_recvbuffs (void)
58 {
59 	return full_recvbufs;
60 }
61 
62 u_long
63 total_recvbuffs (void)
64 {
65 	return total_recvbufs;
66 }
67 
68 u_long
69 lowater_additions(void)
70 {
71 	return lowater_adds;
72 }
73 
74 static inline void
75 initialise_buffer(recvbuf_t *buff)
76 {
77 	ZERO(*buff);
78 }
79 
80 static void
81 create_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_zero(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_zero(sizeof(*bufp));
101 #endif
102 		LINK_SLIST(free_recv_list, bufp, link);
103 		bufp++;
104 		free_recvbufs++;
105 		total_recvbufs++;
106 	}
107 	lowater_adds++;
108 }
109 
110 void
111 init_recvbuff(int nbufs)
112 {
113 
114 	/*
115 	 * Init buffer free list and stat counters
116 	 */
117 	free_recvbufs = total_recvbufs = 0;
118 	full_recvbufs = lowater_adds = 0;
119 
120 	create_buffers(nbufs);
121 
122 #if defined(SYS_WINNT)
123 	InitializeCriticalSection(&RecvLock);
124 #endif
125 
126 #ifdef DEBUG
127 	atexit(&uninit_recvbuff);
128 #endif
129 }
130 
131 
132 #ifdef DEBUG
133 static void
134 uninit_recvbuff(void)
135 {
136 	recvbuf_t *rbunlinked;
137 
138 	do {
139 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
140 		if (rbunlinked != NULL)
141 			free(rbunlinked);
142 	} while (rbunlinked != NULL);
143 
144 	do {
145 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
146 		if (rbunlinked != NULL)
147 			free(rbunlinked);
148 	} while (rbunlinked != NULL);
149 }
150 #endif	/* DEBUG */
151 
152 
153 /*
154  * freerecvbuf - make a single recvbuf available for reuse
155  */
156 void
157 freerecvbuf(recvbuf_t *rb)
158 {
159 	if (rb == NULL) {
160 		msyslog(LOG_ERR, "freerecvbuff received NULL buffer");
161 		return;
162 	}
163 
164 	LOCK();
165 	rb->used--;
166 	if (rb->used != 0)
167 		msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
168 	LINK_SLIST(free_recv_list, rb, link);
169 	free_recvbufs++;
170 	UNLOCK();
171 }
172 
173 
174 void
175 add_full_recv_buffer(recvbuf_t *rb)
176 {
177 	if (rb == NULL) {
178 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
179 		return;
180 	}
181 	LOCK();
182 	LINK_FIFO(full_recv_fifo, rb, link);
183 	full_recvbufs++;
184 	UNLOCK();
185 }
186 
187 
188 recvbuf_t *
189 get_free_recv_buffer(void)
190 {
191 	recvbuf_t *buffer;
192 
193 	LOCK();
194 	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
195 	if (buffer != NULL) {
196 		free_recvbufs--;
197 		initialise_buffer(buffer);
198 		buffer->used++;
199 	} else {
200 		buffer_shortfall++;
201 	}
202 	UNLOCK();
203 
204 	return buffer;
205 }
206 
207 
208 #ifdef HAVE_IO_COMPLETION_PORT
209 recvbuf_t *
210 get_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 
224 
225 recvbuf_t *
226 get_full_recv_buffer(void)
227 {
228 	recvbuf_t *	rbuf;
229 
230 	LOCK();
231 
232 #ifdef HAVE_SIGNALED_IO
233 	/*
234 	 * make sure there are free buffers when we
235 	 * wander off to do lengthy packet processing with
236 	 * any buffer we grab from the full list.
237 	 *
238 	 * fixes malloc() interrupted by SIGIO risk
239 	 * (Bug 889)
240 	 */
241 	if (NULL == free_recv_list || buffer_shortfall > 0) {
242 		/*
243 		 * try to get us some more buffers
244 		 */
245 		create_buffers(RECV_INC);
246 	}
247 #endif
248 
249 	/*
250 	 * try to grab a full buffer
251 	 */
252 	UNLINK_FIFO(rbuf, full_recv_fifo, link);
253 	if (rbuf != NULL)
254 		full_recvbufs--;
255 	UNLOCK();
256 
257 	return rbuf;
258 }
259 
260 
261 /*
262  * purge_recv_buffers_for_fd() - purges any previously-received input
263  *				 from a given file descriptor.
264  */
265 void
266 purge_recv_buffers_for_fd(
267 	SOCKET	fd
268 	)
269 {
270 	recvbuf_t *rbufp;
271 	recvbuf_t *next;
272 	recvbuf_t *punlinked;
273 
274 	LOCK();
275 
276 	for (rbufp = HEAD_FIFO(full_recv_fifo);
277 	     rbufp != NULL;
278 	     rbufp = next) {
279 		next = rbufp->link;
280 		if (rbufp->fd == fd) {
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  */
296 isc_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
306 void
307 check_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