xref: /netbsd-src/external/bsd/ntp/dist/libntp/recvbuff.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: recvbuff.c,v 1.8 2020/05/25 20:47:24 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 = eallocarray(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) {
162 		LOCK();
163 		rb->used--;
164 		if (rb->used != 0)
165 			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
166 		LINK_SLIST(free_recv_list, rb, link);
167 		free_recvbufs++;
168 		UNLOCK();
169 	}
170 }
171 
172 
173 void
174 add_full_recv_buffer(recvbuf_t *rb)
175 {
176 	if (rb == NULL) {
177 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
178 		return;
179 	}
180 	LOCK();
181 	LINK_FIFO(full_recv_fifo, rb, link);
182 	full_recvbufs++;
183 	UNLOCK();
184 }
185 
186 
187 recvbuf_t *
188 get_free_recv_buffer(void)
189 {
190 	recvbuf_t *buffer;
191 
192 	LOCK();
193 	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
194 	if (buffer != NULL) {
195 		free_recvbufs--;
196 		initialise_buffer(buffer);
197 		buffer->used++;
198 	} else {
199 		buffer_shortfall++;
200 	}
201 	UNLOCK();
202 
203 	return buffer;
204 }
205 
206 
207 #ifdef HAVE_IO_COMPLETION_PORT
208 recvbuf_t *
209 get_free_recv_buffer_alloc(void)
210 {
211 	recvbuf_t *buffer;
212 
213 	buffer = get_free_recv_buffer();
214 	if (NULL == buffer) {
215 		create_buffers(RECV_INC);
216 		buffer = get_free_recv_buffer();
217 	}
218 	ENSURE(buffer != NULL);
219 	return (buffer);
220 }
221 #endif
222 
223 
224 recvbuf_t *
225 get_full_recv_buffer(void)
226 {
227 	recvbuf_t *	rbuf;
228 
229 	LOCK();
230 
231 	/*
232 	 * make sure there are free buffers when we
233 	 * wander off to do lengthy packet processing with
234 	 * any buffer we grab from the full list.
235 	 *
236 	 * fixes malloc() interrupted by SIGIO risk
237 	 * (Bug 889)
238 	 */
239 	if (NULL == free_recv_list || buffer_shortfall > 0) {
240 		/*
241 		 * try to get us some more buffers
242 		 */
243 		create_buffers(RECV_INC);
244 	}
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  */
262 void
263 purge_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  */
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