xref: /netbsd/external/bsd/ntp/dist/libntp/recvbuff.c (revision e03b00c8)
1 /*	$NetBSD: recvbuff.c,v 1.9 2022/10/09 21:41:03 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 #if (RECV_INC & (RECV_INC-1))
17 # error RECV_INC not a power of 2!
18 #endif
19 #if (RECV_BATCH & (RECV_BATCH - 1))
20 #error RECV_BATCH not a power of 2!
21 #endif
22 #if (RECV_BATCH < RECV_INC)
23 #error RECV_BATCH must be >= RECV_INC!
24 #endif
25 
26 /*
27  * Memory allocation
28  */
29 static u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
30 static u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
31 static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
32 static u_long volatile lowater_adds;	/* number of times we have added memory */
33 static u_long volatile buffer_shortfall;/* number of missed free receive buffers
34 					   between replenishments */
35 static u_long limit_recvbufs;		/* maximum total of receive buffers */
36 static u_long emerg_recvbufs;		/* emergency/urgent buffers to keep */
37 
38 static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
39 static recvbuf_t *		   free_recv_list;
40 
41 #if defined(SYS_WINNT)
42 
43 /*
44  * For Windows we need to set up a lock to manipulate the
45  * recv buffers to prevent corruption. We keep it lock for as
46  * short a time as possible
47  */
48 static CRITICAL_SECTION RecvLock;
49 static CRITICAL_SECTION FreeLock;
50 # define LOCK_R()	EnterCriticalSection(&RecvLock)
51 # define UNLOCK_R()	LeaveCriticalSection(&RecvLock)
52 # define LOCK_F()	EnterCriticalSection(&FreeLock)
53 # define UNLOCK_F()	LeaveCriticalSection(&FreeLock)
54 #else
55 # define LOCK_R()	do {} while (FALSE)
56 # define UNLOCK_R()	do {} while (FALSE)
57 # define LOCK_F()	do {} while (FALSE)
58 # define UNLOCK_F()	do {} while (FALSE)
59 #endif
60 
61 #ifdef DEBUG
62 static void uninit_recvbuff(void);
63 #endif
64 
65 
66 u_long
free_recvbuffs(void)67 free_recvbuffs (void)
68 {
69 	return free_recvbufs;
70 }
71 
72 u_long
full_recvbuffs(void)73 full_recvbuffs (void)
74 {
75 	return full_recvbufs;
76 }
77 
78 u_long
total_recvbuffs(void)79 total_recvbuffs (void)
80 {
81 	return total_recvbufs;
82 }
83 
84 u_long
lowater_additions(void)85 lowater_additions(void)
86 {
87 	return lowater_adds;
88 }
89 
90 static inline void
initialise_buffer(recvbuf_t * buff)91 initialise_buffer(recvbuf_t *buff)
92 {
93 	ZERO(*buff);
94 }
95 
96 static void
create_buffers(size_t nbufs)97 create_buffers(
98 	size_t		nbufs)
99 {
100 #   ifndef DEBUG
101 	static const u_int chunk = RECV_INC;
102 #   else
103 	/* Allocate each buffer individually so they can be free()d
104 	 * during ntpd shutdown on DEBUG builds to keep them out of heap
105 	 * leak reports.
106 	 */
107 	static const u_int chunk = 1;
108 #   endif
109 
110 	register recvbuf_t *bufp;
111 	u_int i;
112 	size_t abuf;
113 
114 	if (limit_recvbufs <= total_recvbufs)
115 		return;
116 
117 	abuf = nbufs + buffer_shortfall;
118 	buffer_shortfall = 0;
119 
120 	if (abuf < nbufs || abuf > RECV_BATCH)
121 		abuf = RECV_BATCH;	/* clamp on overflow */
122 	else
123 		abuf += (~abuf + 1) & (RECV_INC - 1);	/* round up */
124 
125 	if (abuf > (limit_recvbufs - total_recvbufs))
126 		abuf = limit_recvbufs - total_recvbufs;
127 	abuf += (~abuf + 1) & (chunk - 1);		/* round up */
128 
129 	while (abuf) {
130 		bufp = calloc(chunk, sizeof(*bufp));
131 		if (!bufp) {
132 			limit_recvbufs = total_recvbufs;
133 			break;
134 		}
135 		for (i = chunk; i; --i,++bufp) {
136 			LINK_SLIST(free_recv_list, bufp, link);
137 		}
138 		free_recvbufs += chunk;
139 		total_recvbufs += chunk;
140 		abuf -= chunk;
141 	}
142 	++lowater_adds;
143 }
144 
145 void
init_recvbuff(int nbufs)146 init_recvbuff(int nbufs)
147 {
148 
149 	/*
150 	 * Init buffer free list and stat counters
151 	 */
152 	free_recvbufs = total_recvbufs = 0;
153 	full_recvbufs = lowater_adds = 0;
154 
155 	limit_recvbufs = RECV_TOOMANY;
156 	emerg_recvbufs = RECV_CLOCK;
157 
158 	create_buffers(nbufs);
159 
160 #   if defined(SYS_WINNT)
161 	InitializeCriticalSection(&RecvLock);
162 	InitializeCriticalSection(&FreeLock);
163 #   endif
164 
165 #   ifdef DEBUG
166 	atexit(&uninit_recvbuff);
167 #   endif
168 }
169 
170 
171 #ifdef DEBUG
172 static void
uninit_recvbuff(void)173 uninit_recvbuff(void)
174 {
175 	recvbuf_t *rbunlinked;
176 
177 	for (;;) {
178 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
179 		if (rbunlinked == NULL)
180 			break;
181 		free(rbunlinked);
182 	}
183 
184 	for (;;) {
185 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
186 		if (rbunlinked == NULL)
187 			break;
188 		free(rbunlinked);
189 	}
190 #   if defined(SYS_WINNT)
191 	DeleteCriticalSection(&FreeLock);
192 	DeleteCriticalSection(&RecvLock);
193 #   endif
194 }
195 #endif	/* DEBUG */
196 
197 
198 /*
199  * freerecvbuf - make a single recvbuf available for reuse
200  */
201 void
freerecvbuf(recvbuf_t * rb)202 freerecvbuf(recvbuf_t *rb)
203 {
204 	if (rb) {
205 		if (--rb->used != 0) {
206 			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
207 			rb->used = 0;
208 		}
209 		LOCK_F();
210 		LINK_SLIST(free_recv_list, rb, link);
211 		++free_recvbufs;
212 		UNLOCK_F();
213 	}
214 }
215 
216 
217 void
add_full_recv_buffer(recvbuf_t * rb)218 add_full_recv_buffer(recvbuf_t *rb)
219 {
220 	if (rb == NULL) {
221 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
222 		return;
223 	}
224 	LOCK_R();
225 	LINK_FIFO(full_recv_fifo, rb, link);
226 	++full_recvbufs;
227 	UNLOCK_R();
228 }
229 
230 
231 recvbuf_t *
get_free_recv_buffer(int urgent)232 get_free_recv_buffer(
233     int /*BOOL*/ urgent
234     )
235 {
236 	recvbuf_t *buffer = NULL;
237 
238 	LOCK_F();
239 	if (free_recvbufs > (urgent ? emerg_recvbufs : 0)) {
240 		UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
241 	}
242 
243 	if (buffer != NULL) {
244 		if (free_recvbufs)
245 			--free_recvbufs;
246 		initialise_buffer(buffer);
247 		++buffer->used;
248 	} else {
249 		++buffer_shortfall;
250 	}
251 	UNLOCK_F();
252 
253 	return buffer;
254 }
255 
256 
257 #ifdef HAVE_IO_COMPLETION_PORT
258 recvbuf_t *
get_free_recv_buffer_alloc(int urgent)259 get_free_recv_buffer_alloc(
260     int /*BOOL*/ urgent
261     )
262 {
263 	LOCK_F();
264 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
265 		create_buffers(RECV_INC);
266 	UNLOCK_F();
267 	return get_free_recv_buffer(urgent);
268 }
269 #endif
270 
271 
272 recvbuf_t *
get_full_recv_buffer(void)273 get_full_recv_buffer(void)
274 {
275 	recvbuf_t *	rbuf;
276 
277 	/*
278 	 * make sure there are free buffers when we wander off to do
279 	 * lengthy packet processing with any buffer we grab from the
280 	 * full list.
281 	 *
282 	 * fixes malloc() interrupted by SIGIO risk (Bug 889)
283 	 */
284 	LOCK_F();
285 	if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0)
286 		create_buffers(RECV_INC);
287 	UNLOCK_F();
288 
289 	/*
290 	 * try to grab a full buffer
291 	 */
292 	LOCK_R();
293 	UNLINK_FIFO(rbuf, full_recv_fifo, link);
294 	if (rbuf != NULL && full_recvbufs)
295 		--full_recvbufs;
296 	UNLOCK_R();
297 
298 	return rbuf;
299 }
300 
301 
302 /*
303  * purge_recv_buffers_for_fd() - purges any previously-received input
304  *				 from a given file descriptor.
305  */
306 void
purge_recv_buffers_for_fd(int fd)307 purge_recv_buffers_for_fd(
308 	int	fd
309 	)
310 {
311 	recvbuf_t *rbufp;
312 	recvbuf_t *next;
313 	recvbuf_t *punlinked;
314 	recvbuf_t *freelist = NULL;
315 
316 	/* We want to hold only one lock at a time. So we do a scan on
317 	 * the full buffer queue, collecting items as we go, and when
318 	 * done we spool the the collected items to 'freerecvbuf()'.
319 	 */
320 	LOCK_R();
321 
322 	for (rbufp = HEAD_FIFO(full_recv_fifo);
323 	     rbufp != NULL;
324 	     rbufp = next)
325 	{
326 		next = rbufp->link;
327 #	    ifdef HAVE_IO_COMPLETION_PORT
328 		if (rbufp->dstadr == NULL && rbufp->fd == fd)
329 #	    else
330 		if (rbufp->fd == fd)
331 #	    endif
332 		{
333 			UNLINK_MID_FIFO(punlinked, full_recv_fifo,
334 					rbufp, link, recvbuf_t);
335 			INSIST(punlinked == rbufp);
336 			if (full_recvbufs)
337 				--full_recvbufs;
338 			rbufp->link = freelist;
339 			freelist = rbufp;
340 		}
341 	}
342 
343 	UNLOCK_R();
344 
345 	while (freelist) {
346 		next = freelist->link;
347 		freerecvbuf(freelist);
348 		freelist = next;
349 	}
350 }
351 
352 
353 /*
354  * Checks to see if there are buffers to process
355  */
has_full_recv_buffer(void)356 isc_boolean_t has_full_recv_buffer(void)
357 {
358 	if (HEAD_FIFO(full_recv_fifo) != NULL)
359 		return (ISC_TRUE);
360 	else
361 		return (ISC_FALSE);
362 }
363 
364 
365 #ifdef NTP_DEBUG_LISTS_H
366 void
check_gen_fifo_consistency(void * fifo)367 check_gen_fifo_consistency(void *fifo)
368 {
369 	gen_fifo *pf;
370 	gen_node *pthis;
371 	gen_node **pptail;
372 
373 	pf = fifo;
374 	REQUIRE((NULL == pf->phead && NULL == pf->pptail) ||
375 		(NULL != pf->phead && NULL != pf->pptail));
376 
377 	pptail = &pf->phead;
378 	for (pthis = pf->phead;
379 	     pthis != NULL;
380 	     pthis = pthis->link)
381 		if (NULL != pthis->link)
382 			pptail = &pthis->link;
383 
384 	REQUIRE(NULL == pf->pptail || pptail == pf->pptail);
385 }
386 #endif	/* NTP_DEBUG_LISTS_H */
387