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