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
free_recvbuffs(void)49 free_recvbuffs (void)
50 {
51 return free_recvbufs;
52 }
53
54 u_long
full_recvbuffs(void)55 full_recvbuffs (void)
56 {
57 return full_recvbufs;
58 }
59
60 u_long
total_recvbuffs(void)61 total_recvbuffs (void)
62 {
63 return total_recvbufs;
64 }
65
66 u_long
lowater_additions(void)67 lowater_additions(void)
68 {
69 return lowater_adds;
70 }
71
72 static inline void
initialise_buffer(recvbuf_t * buff)73 initialise_buffer(recvbuf_t *buff)
74 {
75 ZERO(*buff);
76 }
77
78 static void
create_buffers(int nbufs)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
init_recvbuff(int nbufs)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
uninit_recvbuff(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
freerecvbuf(recvbuf_t * rb)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
add_full_recv_buffer(recvbuf_t * rb)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 *
get_free_recv_buffer(void)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 *
get_free_recv_buffer_alloc(void)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 *
get_full_recv_buffer(void)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
purge_recv_buffers_for_fd(int fd)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 */
has_full_recv_buffer(void)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
check_gen_fifo_consistency(void * fifo)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