xref: /freebsd/contrib/ntp/libntp/recvbuff.c (revision d93a896ef95946b0bf1219866fcb324b78543444)
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <stdio.h>
6 
7 #include "ntp_assert.h"
8 #include "ntp_syslog.h"
9 #include "ntp_stdlib.h"
10 #include "ntp_lists.h"
11 #include "recvbuff.h"
12 #include "iosignal.h"
13 
14 
15 /*
16  * Memory allocation
17  */
18 static u_long volatile full_recvbufs;	/* recvbufs on full_recv_fifo */
19 static u_long volatile free_recvbufs;	/* recvbufs on free_recv_list */
20 static u_long volatile total_recvbufs;	/* total recvbufs currently in use */
21 static u_long volatile lowater_adds;	/* number of times we have added memory */
22 static u_long volatile buffer_shortfall;/* number of missed free receive buffers
23 					   between replenishments */
24 
25 static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo;
26 static recvbuf_t *		   free_recv_list;
27 
28 #if defined(SYS_WINNT)
29 
30 /*
31  * For Windows we need to set up a lock to manipulate the
32  * recv buffers to prevent corruption. We keep it lock for as
33  * short a time as possible
34  */
35 static CRITICAL_SECTION RecvLock;
36 # define LOCK()		EnterCriticalSection(&RecvLock)
37 # define UNLOCK()	LeaveCriticalSection(&RecvLock)
38 #else
39 # define LOCK()		do {} while (FALSE)
40 # define UNLOCK()	do {} while (FALSE)
41 #endif
42 
43 #ifdef DEBUG
44 static void uninit_recvbuff(void);
45 #endif
46 
47 
48 u_long
49 free_recvbuffs (void)
50 {
51 	return free_recvbufs;
52 }
53 
54 u_long
55 full_recvbuffs (void)
56 {
57 	return full_recvbufs;
58 }
59 
60 u_long
61 total_recvbuffs (void)
62 {
63 	return total_recvbufs;
64 }
65 
66 u_long
67 lowater_additions(void)
68 {
69 	return lowater_adds;
70 }
71 
72 static inline void
73 initialise_buffer(recvbuf_t *buff)
74 {
75 	ZERO(*buff);
76 }
77 
78 static void
79 create_buffers(int nbufs)
80 {
81 	register recvbuf_t *bufp;
82 	int i, abuf;
83 
84 	abuf = nbufs + buffer_shortfall;
85 	buffer_shortfall = 0;
86 
87 #ifndef DEBUG
88 	bufp = eallocarray(abuf, sizeof(*bufp));
89 #endif
90 
91 	for (i = 0; i < abuf; i++) {
92 #ifdef DEBUG
93 		/*
94 		 * Allocate each buffer individually so they can be
95 		 * free()d during ntpd shutdown on DEBUG builds to
96 		 * keep them out of heap leak reports.
97 		 */
98 		bufp = emalloc_zero(sizeof(*bufp));
99 #endif
100 		LINK_SLIST(free_recv_list, bufp, link);
101 		bufp++;
102 		free_recvbufs++;
103 		total_recvbufs++;
104 	}
105 	lowater_adds++;
106 }
107 
108 void
109 init_recvbuff(int nbufs)
110 {
111 
112 	/*
113 	 * Init buffer free list and stat counters
114 	 */
115 	free_recvbufs = total_recvbufs = 0;
116 	full_recvbufs = lowater_adds = 0;
117 
118 	create_buffers(nbufs);
119 
120 #if defined(SYS_WINNT)
121 	InitializeCriticalSection(&RecvLock);
122 #endif
123 
124 #ifdef DEBUG
125 	atexit(&uninit_recvbuff);
126 #endif
127 }
128 
129 
130 #ifdef DEBUG
131 static void
132 uninit_recvbuff(void)
133 {
134 	recvbuf_t *rbunlinked;
135 
136 	for (;;) {
137 		UNLINK_FIFO(rbunlinked, full_recv_fifo, link);
138 		if (rbunlinked == NULL)
139 			break;
140 		free(rbunlinked);
141 	}
142 
143 	for (;;) {
144 		UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link);
145 		if (rbunlinked == NULL)
146 			break;
147 		free(rbunlinked);
148 	}
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) {
160 		LOCK();
161 		rb->used--;
162 		if (rb->used != 0)
163 			msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used);
164 		LINK_SLIST(free_recv_list, rb, link);
165 		free_recvbufs++;
166 		UNLOCK();
167 	}
168 }
169 
170 
171 void
172 add_full_recv_buffer(recvbuf_t *rb)
173 {
174 	if (rb == NULL) {
175 		msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer");
176 		return;
177 	}
178 	LOCK();
179 	LINK_FIFO(full_recv_fifo, rb, link);
180 	full_recvbufs++;
181 	UNLOCK();
182 }
183 
184 
185 recvbuf_t *
186 get_free_recv_buffer(void)
187 {
188 	recvbuf_t *buffer;
189 
190 	LOCK();
191 	UNLINK_HEAD_SLIST(buffer, free_recv_list, link);
192 	if (buffer != NULL) {
193 		free_recvbufs--;
194 		initialise_buffer(buffer);
195 		buffer->used++;
196 	} else {
197 		buffer_shortfall++;
198 	}
199 	UNLOCK();
200 
201 	return buffer;
202 }
203 
204 
205 #ifdef HAVE_IO_COMPLETION_PORT
206 recvbuf_t *
207 get_free_recv_buffer_alloc(void)
208 {
209 	recvbuf_t *buffer;
210 
211 	buffer = get_free_recv_buffer();
212 	if (NULL == buffer) {
213 		create_buffers(RECV_INC);
214 		buffer = get_free_recv_buffer();
215 	}
216 	ENSURE(buffer != NULL);
217 	return (buffer);
218 }
219 #endif
220 
221 
222 recvbuf_t *
223 get_full_recv_buffer(void)
224 {
225 	recvbuf_t *	rbuf;
226 
227 	LOCK();
228 
229 #ifdef HAVE_SIGNALED_IO
230 	/*
231 	 * make sure there are free buffers when we
232 	 * wander off to do lengthy packet processing with
233 	 * any buffer we grab from the full list.
234 	 *
235 	 * fixes malloc() interrupted by SIGIO risk
236 	 * (Bug 889)
237 	 */
238 	if (NULL == free_recv_list || buffer_shortfall > 0) {
239 		/*
240 		 * try to get us some more buffers
241 		 */
242 		create_buffers(RECV_INC);
243 	}
244 #endif
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