xref: /freebsd/contrib/ntp/libntp/recvbuff.c (revision 895f86f15fbf6540071feb9328c3c50ed1f027b8)
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 = emalloc_zero(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 == 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 	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