xref: /netbsd-src/external/bsd/ntp/dist/libntp/recvbuff.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: recvbuff.c,v 1.5 2016/01/08 21:35:38 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 	for (;;) {
139 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
140 		if (rbunlinked == NULL)
141 			break;
142 		free(rbunlinked);
143 	}
144 
145 	for (;;) {
146 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
147 		if (rbunlinked == NULL)
148 			break;
149 		free(rbunlinked);
150 	}
151 }
152 #endif	/* DEBUG */
153 
154 
155 /*
156  * freerecvbuf - make a single recvbuf available for reuse
157  */
158 void
159 freerecvbuf(recvbuf_t *rb)
160 {
161 	if (rb == NULL) {
162 		msyslog(LOG_ERR, "freerecvbuff received NULL buffer");
163 		return;
164 	}
165 
166 	LOCK();
167 	rb->used--;
168 	if (rb->used != 0)
169 		msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
170 	LINK_SLIST(free_recv_list, rb, link);
171 	free_recvbufs++;
172 	UNLOCK();
173 }
174 
175 
176 void
177 add_full_recv_buffer(recvbuf_t *rb)
178 {
179 	if (rb == NULL) {
180 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
181 		return;
182 	}
183 	LOCK();
184 	LINK_FIFO(full_recv_fifo, rb, link);
185 	full_recvbufs++;
186 	UNLOCK();
187 }
188 
189 
190 recvbuf_t *
191 get_free_recv_buffer(void)
192 {
193 	recvbuf_t *buffer;
194 
195 	LOCK();
196 	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
197 	if (buffer != NULL) {
198 		free_recvbufs--;
199 		initialise_buffer(buffer);
200 		buffer->used++;
201 	} else {
202 		buffer_shortfall++;
203 	}
204 	UNLOCK();
205 
206 	return buffer;
207 }
208 
209 
210 #ifdef HAVE_IO_COMPLETION_PORT
211 recvbuf_t *
212 get_free_recv_buffer_alloc(void)
213 {
214 	recvbuf_t *buffer;
215 
216 	buffer = get_free_recv_buffer();
217 	if (NULL == buffer) {
218 		create_buffers(RECV_INC);
219 		buffer = get_free_recv_buffer();
220 	}
221 	ENSURE(buffer != NULL);
222 	return (buffer);
223 }
224 #endif
225 
226 
227 recvbuf_t *
228 get_full_recv_buffer(void)
229 {
230 	recvbuf_t *	rbuf;
231 
232 	LOCK();
233 
234 #ifdef HAVE_SIGNALED_IO
235 	/*
236 	 * make sure there are free buffers when we
237 	 * wander off to do lengthy packet processing with
238 	 * any buffer we grab from the full list.
239 	 *
240 	 * fixes malloc() interrupted by SIGIO risk
241 	 * (Bug 889)
242 	 */
243 	if (NULL == free_recv_list || buffer_shortfall > 0) {
244 		/*
245 		 * try to get us some more buffers
246 		 */
247 		create_buffers(RECV_INC);
248 	}
249 #endif
250 
251 	/*
252 	 * try to grab a full buffer
253 	 */
254 	UNLINK_FIFO(rbuf, full_recv_fifo, link);
255 	if (rbuf != NULL)
256 		full_recvbufs--;
257 	UNLOCK();
258 
259 	return rbuf;
260 }
261 
262 
263 /*
264  * purge_recv_buffers_for_fd() - purges any previously-received input
265  *				 from a given file descriptor.
266  */
267 void
268 purge_recv_buffers_for_fd(
269 	SOCKET	fd
270 	)
271 {
272 	recvbuf_t *rbufp;
273 	recvbuf_t *next;
274 	recvbuf_t *punlinked;
275 
276 	LOCK();
277 
278 	for (rbufp = HEAD_FIFO(full_recv_fifo);
279 	     rbufp != NULL;
280 	     rbufp = next) {
281 		next = rbufp->link;
282 		if (rbufp->fd == fd) {
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  */
298 isc_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
308 void
309 check_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