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 */ 29static u_long volatile full_recvbufs; /* recvbufs on full_recv_fifo */ 30static u_long volatile free_recvbufs; /* recvbufs on free_recv_list */ 31static u_long volatile total_recvbufs; /* total recvbufs currently in use */ 32static u_long volatile lowater_adds; /* number of times we have added memory */ 33static u_long volatile buffer_shortfall;/* number of missed free receive buffers 34 between replenishments */ 35static u_long limit_recvbufs; /* maximum total of receive buffers */ 36static u_long emerg_recvbufs; /* emergency/urgent buffers to keep */ 37 38static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo; 39static 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 */ 48static CRITICAL_SECTION RecvLock; 49static 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 62static void uninit_recvbuff(void); 63#endif 64 65 66u_long 67free_recvbuffs (void) 68{ 69 return free_recvbufs; 70} 71 72u_long 73full_recvbuffs (void) 74{ 75 return full_recvbufs; 76} 77 78u_long 79total_recvbuffs (void) 80{ 81 return total_recvbufs; 82} 83 84u_long 85lowater_additions(void) 86{ 87 return lowater_adds; 88} 89 90static inline void 91initialise_buffer(recvbuf_t *buff) 92{ 93 ZERO(*buff); 94} 95 96static void 97create_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 145void 146init_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 172static void 173uninit_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 */ 201void 202freerecvbuf(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 217void 218add_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 231recvbuf_t * 232get_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 258recvbuf_t * 259get_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 272recvbuf_t * 273get_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 */ 306void 307purge_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 */ 356isc_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 366void 367check_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