1c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 2c0b746e5SOllivier Robert # include <config.h> 3c0b746e5SOllivier Robert #endif 4c0b746e5SOllivier Robert 5c0b746e5SOllivier Robert #include <stdio.h> 62b15cb3dSCy Schubert 72b15cb3dSCy Schubert #include "ntp_assert.h" 8c0b746e5SOllivier Robert #include "ntp_syslog.h" 9224ba2bdSOllivier Robert #include "ntp_stdlib.h" 102b15cb3dSCy Schubert #include "ntp_lists.h" 11c0b746e5SOllivier Robert #include "recvbuff.h" 12c0b746e5SOllivier Robert #include "iosignal.h" 13c0b746e5SOllivier Robert 14767173ceSCy Schubert #if (RECV_INC & (RECV_INC-1)) 15767173ceSCy Schubert # error RECV_INC not a power of 2! 16767173ceSCy Schubert #endif 17767173ceSCy Schubert #if (RECV_BATCH & (RECV_BATCH - 1)) 18767173ceSCy Schubert #error RECV_BATCH not a power of 2! 19767173ceSCy Schubert #endif 20767173ceSCy Schubert #if (RECV_BATCH < RECV_INC) 21767173ceSCy Schubert #error RECV_BATCH must be >= RECV_INC! 22767173ceSCy Schubert #endif 232b15cb3dSCy Schubert 24c0b746e5SOllivier Robert /* 25c0b746e5SOllivier Robert * Memory allocation 26c0b746e5SOllivier Robert */ 272b15cb3dSCy Schubert static u_long volatile full_recvbufs; /* recvbufs on full_recv_fifo */ 282b15cb3dSCy Schubert static u_long volatile free_recvbufs; /* recvbufs on free_recv_list */ 29c0b746e5SOllivier Robert static u_long volatile total_recvbufs; /* total recvbufs currently in use */ 30c0b746e5SOllivier Robert static u_long volatile lowater_adds; /* number of times we have added memory */ 31ea906c41SOllivier Robert static u_long volatile buffer_shortfall;/* number of missed free receive buffers 32ea906c41SOllivier Robert between replenishments */ 33767173ceSCy Schubert static u_long limit_recvbufs; /* maximum total of receive buffers */ 34767173ceSCy Schubert static u_long emerg_recvbufs; /* emergency/urgent buffers to keep */ 35c0b746e5SOllivier Robert 362b15cb3dSCy Schubert static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo; 372b15cb3dSCy Schubert static recvbuf_t * free_recv_list; 38c0b746e5SOllivier Robert 39ea906c41SOllivier Robert #if defined(SYS_WINNT) 40ea906c41SOllivier Robert 41ea906c41SOllivier Robert /* 42ea906c41SOllivier Robert * For Windows we need to set up a lock to manipulate the 43ea906c41SOllivier Robert * recv buffers to prevent corruption. We keep it lock for as 44ea906c41SOllivier Robert * short a time as possible 45ea906c41SOllivier Robert */ 46ea906c41SOllivier Robert static CRITICAL_SECTION RecvLock; 47767173ceSCy Schubert static CRITICAL_SECTION FreeLock; 48767173ceSCy Schubert # define LOCK_R() EnterCriticalSection(&RecvLock) 49767173ceSCy Schubert # define UNLOCK_R() LeaveCriticalSection(&RecvLock) 50767173ceSCy Schubert # define LOCK_F() EnterCriticalSection(&FreeLock) 51767173ceSCy Schubert # define UNLOCK_F() LeaveCriticalSection(&FreeLock) 52c0b746e5SOllivier Robert #else 53767173ceSCy Schubert # define LOCK_R() do {} while (FALSE) 54767173ceSCy Schubert # define UNLOCK_R() do {} while (FALSE) 55767173ceSCy Schubert # define LOCK_F() do {} while (FALSE) 56767173ceSCy Schubert # define UNLOCK_F() do {} while (FALSE) 57c0b746e5SOllivier Robert #endif 58c0b746e5SOllivier Robert 592b15cb3dSCy Schubert #ifdef DEBUG 602b15cb3dSCy Schubert static void uninit_recvbuff(void); 612b15cb3dSCy Schubert #endif 622b15cb3dSCy Schubert 632b15cb3dSCy Schubert 64c0b746e5SOllivier Robert u_long 65c0b746e5SOllivier Robert free_recvbuffs (void) 66c0b746e5SOllivier Robert { 67c0b746e5SOllivier Robert return free_recvbufs; 68c0b746e5SOllivier Robert } 69c0b746e5SOllivier Robert 70c0b746e5SOllivier Robert u_long 71c0b746e5SOllivier Robert full_recvbuffs (void) 72c0b746e5SOllivier Robert { 73ea906c41SOllivier Robert return full_recvbufs; 74c0b746e5SOllivier Robert } 75c0b746e5SOllivier Robert 76c0b746e5SOllivier Robert u_long 77c0b746e5SOllivier Robert total_recvbuffs (void) 78c0b746e5SOllivier Robert { 79ea906c41SOllivier Robert return total_recvbufs; 80c0b746e5SOllivier Robert } 81c0b746e5SOllivier Robert 82c0b746e5SOllivier Robert u_long 83c0b746e5SOllivier Robert lowater_additions(void) 84c0b746e5SOllivier Robert { 85c0b746e5SOllivier Robert return lowater_adds; 86c0b746e5SOllivier Robert } 87c0b746e5SOllivier Robert 882b15cb3dSCy Schubert static inline void 89ea906c41SOllivier Robert initialise_buffer(recvbuf_t *buff) 90c0b746e5SOllivier Robert { 912b15cb3dSCy Schubert ZERO(*buff); 92c0b746e5SOllivier Robert } 93c0b746e5SOllivier Robert 94c0b746e5SOllivier Robert static void 95767173ceSCy Schubert create_buffers( 96*f5f40dd6SCy Schubert size_t nbufs 97*f5f40dd6SCy Schubert ) 98c0b746e5SOllivier Robert { 99*f5f40dd6SCy Schubert static const u_int chunk = 100767173ceSCy Schubert # ifndef DEBUG 101*f5f40dd6SCy Schubert RECV_INC; 102767173ceSCy Schubert # else 103767173ceSCy Schubert /* Allocate each buffer individually so they can be free()d 104767173ceSCy Schubert * during ntpd shutdown on DEBUG builds to keep them out of heap 105767173ceSCy Schubert * leak reports. 106767173ceSCy Schubert */ 107*f5f40dd6SCy Schubert 1; 108767173ceSCy Schubert # endif 109*f5f40dd6SCy Schubert static int/*BOOL*/ doneonce; 110*f5f40dd6SCy Schubert recvbuf_t * bufp; 111767173ceSCy Schubert u_int i; 112767173ceSCy Schubert size_t abuf; 113767173ceSCy Schubert 114a466cc55SCy Schubert /*[bug 3666]: followup -- reset shortfalls in all cases */ 115ea906c41SOllivier Robert abuf = nbufs + buffer_shortfall; 116ea906c41SOllivier Robert buffer_shortfall = 0; 117ea906c41SOllivier Robert 118*f5f40dd6SCy Schubert if (limit_recvbufs <= total_recvbufs) { 119*f5f40dd6SCy Schubert if (!doneonce) { 120*f5f40dd6SCy Schubert msyslog(LOG_CRIT, "Unable to allocate receive" 121*f5f40dd6SCy Schubert " buffer, %lu/%lu", 122*f5f40dd6SCy Schubert total_recvbufs, limit_recvbufs); 123*f5f40dd6SCy Schubert doneonce = TRUE; 124*f5f40dd6SCy Schubert } 125a466cc55SCy Schubert return; 126*f5f40dd6SCy Schubert } 127a466cc55SCy Schubert 128*f5f40dd6SCy Schubert if (abuf < nbufs || abuf > RECV_BATCH) { 129767173ceSCy Schubert abuf = RECV_BATCH; /* clamp on overflow */ 130*f5f40dd6SCy Schubert } else { 131767173ceSCy Schubert abuf += (~abuf + 1) & (RECV_INC - 1); /* round up */ 132*f5f40dd6SCy Schubert } 133*f5f40dd6SCy Schubert if (abuf > (limit_recvbufs - total_recvbufs)) { 134767173ceSCy Schubert abuf = limit_recvbufs - total_recvbufs; 135*f5f40dd6SCy Schubert } 136767173ceSCy Schubert abuf += (~abuf + 1) & (chunk - 1); /* round up */ 137767173ceSCy Schubert 138767173ceSCy Schubert while (abuf) { 139767173ceSCy Schubert bufp = calloc(chunk, sizeof(*bufp)); 140767173ceSCy Schubert if (!bufp) { 141*f5f40dd6SCy Schubert msyslog(LOG_CRIT, "Out of memory, allocating " 142*f5f40dd6SCy Schubert "%u recvbufs, %lu bytes", 143*f5f40dd6SCy Schubert chunk, (u_long)sizeof(*bufp) * chunk); 144767173ceSCy Schubert limit_recvbufs = total_recvbufs; 145767173ceSCy Schubert break; 146ea906c41SOllivier Robert } 147767173ceSCy Schubert for (i = chunk; i; --i,++bufp) { 148767173ceSCy Schubert LINK_SLIST(free_recv_list, bufp, link); 149767173ceSCy Schubert } 150767173ceSCy Schubert free_recvbufs += chunk; 151767173ceSCy Schubert total_recvbufs += chunk; 152767173ceSCy Schubert abuf -= chunk; 153767173ceSCy Schubert } 154767173ceSCy Schubert ++lowater_adds; 155c0b746e5SOllivier Robert } 156c0b746e5SOllivier Robert 157c0b746e5SOllivier Robert void 158c0b746e5SOllivier Robert init_recvbuff(int nbufs) 159c0b746e5SOllivier Robert { 160c0b746e5SOllivier Robert 161c0b746e5SOllivier Robert /* 162c0b746e5SOllivier Robert * Init buffer free list and stat counters 163c0b746e5SOllivier Robert */ 164ea906c41SOllivier Robert free_recvbufs = total_recvbufs = 0; 165c0b746e5SOllivier Robert full_recvbufs = lowater_adds = 0; 166c0b746e5SOllivier Robert 167767173ceSCy Schubert limit_recvbufs = RECV_TOOMANY; 168767173ceSCy Schubert emerg_recvbufs = RECV_CLOCK; 169767173ceSCy Schubert 170ea906c41SOllivier Robert create_buffers(nbufs); 171ea906c41SOllivier Robert 172ea906c41SOllivier Robert # if defined(SYS_WINNT) 173ea906c41SOllivier Robert InitializeCriticalSection(&RecvLock); 174767173ceSCy Schubert InitializeCriticalSection(&FreeLock); 175c0b746e5SOllivier Robert # endif 176c0b746e5SOllivier Robert 1772b15cb3dSCy Schubert # ifdef DEBUG 1782b15cb3dSCy Schubert atexit(&uninit_recvbuff); 1792b15cb3dSCy Schubert # endif 180c0b746e5SOllivier Robert } 181c0b746e5SOllivier Robert 1822b15cb3dSCy Schubert 1832b15cb3dSCy Schubert #ifdef DEBUG 1842b15cb3dSCy Schubert static void 1852b15cb3dSCy Schubert uninit_recvbuff(void) 1862b15cb3dSCy Schubert { 1872b15cb3dSCy Schubert recvbuf_t *rbunlinked; 1882b15cb3dSCy Schubert 1892b15cb3dSCy Schubert for (;;) { 1902b15cb3dSCy Schubert UNLINK_FIFO(rbunlinked, full_recv_fifo, link); 1912b15cb3dSCy Schubert if (rbunlinked == NULL) 1922b15cb3dSCy Schubert break; 1932b15cb3dSCy Schubert free(rbunlinked); 1942b15cb3dSCy Schubert } 1952b15cb3dSCy Schubert 1962b15cb3dSCy Schubert for (;;) { 1972b15cb3dSCy Schubert UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link); 1982b15cb3dSCy Schubert if (rbunlinked == NULL) 1992b15cb3dSCy Schubert break; 2002b15cb3dSCy Schubert free(rbunlinked); 2012b15cb3dSCy Schubert } 202767173ceSCy Schubert # if defined(SYS_WINNT) 203767173ceSCy Schubert DeleteCriticalSection(&FreeLock); 204767173ceSCy Schubert DeleteCriticalSection(&RecvLock); 205767173ceSCy Schubert # endif 2062b15cb3dSCy Schubert } 2072b15cb3dSCy Schubert #endif /* DEBUG */ 2082b15cb3dSCy Schubert 2092b15cb3dSCy Schubert 210c0b746e5SOllivier Robert /* 211c0b746e5SOllivier Robert * freerecvbuf - make a single recvbuf available for reuse 212c0b746e5SOllivier Robert */ 213c0b746e5SOllivier Robert void 214ea906c41SOllivier Robert freerecvbuf(recvbuf_t *rb) 215c0b746e5SOllivier Robert { 2164990d495SXin LI if (rb) { 217767173ceSCy Schubert if (--rb->used != 0) { 218ea906c41SOllivier Robert msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used); 219767173ceSCy Schubert rb->used = 0; 220767173ceSCy Schubert } 221767173ceSCy Schubert LOCK_F(); 2222b15cb3dSCy Schubert LINK_SLIST(free_recv_list, rb, link); 223767173ceSCy Schubert ++free_recvbufs; 224767173ceSCy Schubert UNLOCK_F(); 225c0b746e5SOllivier Robert } 2264990d495SXin LI } 227c0b746e5SOllivier Robert 228c0b746e5SOllivier Robert 229c0b746e5SOllivier Robert void 230ea906c41SOllivier Robert add_full_recv_buffer(recvbuf_t *rb) 231c0b746e5SOllivier Robert { 232ea906c41SOllivier Robert if (rb == NULL) { 233ea906c41SOllivier Robert msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer"); 234ea906c41SOllivier Robert return; 235c0b746e5SOllivier Robert } 236767173ceSCy Schubert LOCK_R(); 2372b15cb3dSCy Schubert LINK_FIFO(full_recv_fifo, rb, link); 238767173ceSCy Schubert ++full_recvbufs; 239767173ceSCy Schubert UNLOCK_R(); 240c0b746e5SOllivier Robert } 241c0b746e5SOllivier Robert 2422b15cb3dSCy Schubert 243ea906c41SOllivier Robert recvbuf_t * 244767173ceSCy Schubert get_free_recv_buffer( 245767173ceSCy Schubert int /*BOOL*/ urgent 246767173ceSCy Schubert ) 247c0b746e5SOllivier Robert { 248767173ceSCy Schubert recvbuf_t *buffer = NULL; 2492b15cb3dSCy Schubert 250767173ceSCy Schubert LOCK_F(); 251a466cc55SCy Schubert if (free_recvbufs > (urgent ? 0 : emerg_recvbufs)) { 2522b15cb3dSCy Schubert UNLINK_HEAD_SLIST(buffer, free_recv_list, link); 253c0b746e5SOllivier Robert } 254767173ceSCy Schubert 255767173ceSCy Schubert if (buffer != NULL) { 256767173ceSCy Schubert if (free_recvbufs) 257767173ceSCy Schubert --free_recvbufs; 258767173ceSCy Schubert initialise_buffer(buffer); 259767173ceSCy Schubert ++buffer->used; 260767173ceSCy Schubert } else { 261767173ceSCy Schubert ++buffer_shortfall; 262767173ceSCy Schubert } 263767173ceSCy Schubert UNLOCK_F(); 2642b15cb3dSCy Schubert 2652b15cb3dSCy Schubert return buffer; 266c0b746e5SOllivier Robert } 267c0b746e5SOllivier Robert 2682b15cb3dSCy Schubert 269ea906c41SOllivier Robert #ifdef HAVE_IO_COMPLETION_PORT 270ea906c41SOllivier Robert recvbuf_t * 271767173ceSCy Schubert get_free_recv_buffer_alloc( 272767173ceSCy Schubert int /*BOOL*/ urgent 273767173ceSCy Schubert ) 274c0b746e5SOllivier Robert { 275767173ceSCy Schubert LOCK_F(); 276767173ceSCy Schubert if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0) 277ea906c41SOllivier Robert create_buffers(RECV_INC); 278767173ceSCy Schubert UNLOCK_F(); 279767173ceSCy Schubert return get_free_recv_buffer(urgent); 280c0b746e5SOllivier Robert } 281ea906c41SOllivier Robert #endif 282c0b746e5SOllivier Robert 2832b15cb3dSCy Schubert 284ea906c41SOllivier Robert recvbuf_t * 285c0b746e5SOllivier Robert get_full_recv_buffer(void) 286c0b746e5SOllivier Robert { 287ea906c41SOllivier Robert recvbuf_t * rbuf; 2882b15cb3dSCy Schubert 289ea906c41SOllivier Robert /* 290767173ceSCy Schubert * make sure there are free buffers when we wander off to do 291767173ceSCy Schubert * lengthy packet processing with any buffer we grab from the 292767173ceSCy Schubert * full list. 293ea906c41SOllivier Robert * 294767173ceSCy Schubert * fixes malloc() interrupted by SIGIO risk (Bug 889) 295ea906c41SOllivier Robert */ 296767173ceSCy Schubert LOCK_F(); 297767173ceSCy Schubert if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0) 298ea906c41SOllivier Robert create_buffers(RECV_INC); 299767173ceSCy Schubert UNLOCK_F(); 300ea906c41SOllivier Robert 301ea906c41SOllivier Robert /* 302ea906c41SOllivier Robert * try to grab a full buffer 303ea906c41SOllivier Robert */ 304767173ceSCy Schubert LOCK_R(); 3052b15cb3dSCy Schubert UNLINK_FIFO(rbuf, full_recv_fifo, link); 306767173ceSCy Schubert if (rbuf != NULL && full_recvbufs) 307767173ceSCy Schubert --full_recvbufs; 308767173ceSCy Schubert UNLOCK_R(); 3092b15cb3dSCy Schubert 3102b15cb3dSCy Schubert return rbuf; 311ea906c41SOllivier Robert } 312ea906c41SOllivier Robert 3132b15cb3dSCy Schubert 3142b15cb3dSCy Schubert /* 3152b15cb3dSCy Schubert * purge_recv_buffers_for_fd() - purges any previously-received input 3162b15cb3dSCy Schubert * from a given file descriptor. 3172b15cb3dSCy Schubert */ 3182b15cb3dSCy Schubert void 3192b15cb3dSCy Schubert purge_recv_buffers_for_fd( 3204990d495SXin LI int fd 3212b15cb3dSCy Schubert ) 3222b15cb3dSCy Schubert { 3232b15cb3dSCy Schubert recvbuf_t *rbufp; 3242b15cb3dSCy Schubert recvbuf_t *next; 3252b15cb3dSCy Schubert recvbuf_t *punlinked; 326767173ceSCy Schubert recvbuf_t *freelist = NULL; 3272b15cb3dSCy Schubert 328767173ceSCy Schubert /* We want to hold only one lock at a time. So we do a scan on 329767173ceSCy Schubert * the full buffer queue, collecting items as we go, and when 330767173ceSCy Schubert * done we spool the the collected items to 'freerecvbuf()'. 331767173ceSCy Schubert */ 332767173ceSCy Schubert LOCK_R(); 3332b15cb3dSCy Schubert 3342b15cb3dSCy Schubert for (rbufp = HEAD_FIFO(full_recv_fifo); 3352b15cb3dSCy Schubert rbufp != NULL; 336767173ceSCy Schubert rbufp = next) 337767173ceSCy Schubert { 3382b15cb3dSCy Schubert next = rbufp->link; 3394990d495SXin LI # ifdef HAVE_IO_COMPLETION_PORT 3404990d495SXin LI if (rbufp->dstadr == NULL && rbufp->fd == fd) 3414990d495SXin LI # else 3424990d495SXin LI if (rbufp->fd == fd) 3434990d495SXin LI # endif 3444990d495SXin LI { 3452b15cb3dSCy Schubert UNLINK_MID_FIFO(punlinked, full_recv_fifo, 3462b15cb3dSCy Schubert rbufp, link, recvbuf_t); 3472b15cb3dSCy Schubert INSIST(punlinked == rbufp); 348767173ceSCy Schubert if (full_recvbufs) 349767173ceSCy Schubert --full_recvbufs; 350767173ceSCy Schubert rbufp->link = freelist; 351767173ceSCy Schubert freelist = rbufp; 3522b15cb3dSCy Schubert } 3532b15cb3dSCy Schubert } 3542b15cb3dSCy Schubert 355767173ceSCy Schubert UNLOCK_R(); 356767173ceSCy Schubert 357767173ceSCy Schubert while (freelist) { 358767173ceSCy Schubert next = freelist->link; 359767173ceSCy Schubert freerecvbuf(freelist); 360767173ceSCy Schubert freelist = next; 361767173ceSCy Schubert } 3622b15cb3dSCy Schubert } 3632b15cb3dSCy Schubert 3642b15cb3dSCy Schubert 365ea906c41SOllivier Robert /* 366ea906c41SOllivier Robert * Checks to see if there are buffers to process 367ea906c41SOllivier Robert */ 368ea906c41SOllivier Robert isc_boolean_t has_full_recv_buffer(void) 369ea906c41SOllivier Robert { 3702b15cb3dSCy Schubert if (HEAD_FIFO(full_recv_fifo) != NULL) 371ea906c41SOllivier Robert return (ISC_TRUE); 372ea906c41SOllivier Robert else 373ea906c41SOllivier Robert return (ISC_FALSE); 374c0b746e5SOllivier Robert } 3752b15cb3dSCy Schubert 3762b15cb3dSCy Schubert 3772b15cb3dSCy Schubert #ifdef NTP_DEBUG_LISTS_H 3782b15cb3dSCy Schubert void 3792b15cb3dSCy Schubert check_gen_fifo_consistency(void *fifo) 3802b15cb3dSCy Schubert { 3812b15cb3dSCy Schubert gen_fifo *pf; 3822b15cb3dSCy Schubert gen_node *pthis; 3832b15cb3dSCy Schubert gen_node **pptail; 3842b15cb3dSCy Schubert 3852b15cb3dSCy Schubert pf = fifo; 3862b15cb3dSCy Schubert REQUIRE((NULL == pf->phead && NULL == pf->pptail) || 3872b15cb3dSCy Schubert (NULL != pf->phead && NULL != pf->pptail)); 3882b15cb3dSCy Schubert 3892b15cb3dSCy Schubert pptail = &pf->phead; 3902b15cb3dSCy Schubert for (pthis = pf->phead; 3912b15cb3dSCy Schubert pthis != NULL; 3922b15cb3dSCy Schubert pthis = pthis->link) 3932b15cb3dSCy Schubert if (NULL != pthis->link) 3942b15cb3dSCy Schubert pptail = &pthis->link; 3952b15cb3dSCy Schubert 3962b15cb3dSCy Schubert REQUIRE(NULL == pf->pptail || pptail == pf->pptail); 3972b15cb3dSCy Schubert } 3982b15cb3dSCy Schubert #endif /* NTP_DEBUG_LISTS_H */ 399