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