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 67 free_recvbuffs (void) 68 { 69 return free_recvbufs; 70 } 71 72 u_long 73 full_recvbuffs (void) 74 { 75 return full_recvbufs; 76 } 77 78 u_long 79 total_recvbuffs (void) 80 { 81 return total_recvbufs; 82 } 83 84 u_long 85 lowater_additions(void) 86 { 87 return lowater_adds; 88 } 89 90 static inline void 91 initialise_buffer(recvbuf_t *buff) 92 { 93 ZERO(*buff); 94 } 95 96 static void 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 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 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 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 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 * 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 * 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 * 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 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 */ 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 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