1 /* $NetBSD: recvbuff.c,v 1.5 2016/01/08 21:35:38 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 17 /* 18 * Memory allocation 19 */ 20 static u_long volatile full_recvbufs; /* recvbufs on full_recv_fifo */ 21 static u_long volatile free_recvbufs; /* recvbufs on free_recv_list */ 22 static u_long volatile total_recvbufs; /* total recvbufs currently in use */ 23 static u_long volatile lowater_adds; /* number of times we have added memory */ 24 static u_long volatile buffer_shortfall;/* number of missed free receive buffers 25 between replenishments */ 26 27 static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo; 28 static recvbuf_t * free_recv_list; 29 30 #if defined(SYS_WINNT) 31 32 /* 33 * For Windows we need to set up a lock to manipulate the 34 * recv buffers to prevent corruption. We keep it lock for as 35 * short a time as possible 36 */ 37 static CRITICAL_SECTION RecvLock; 38 # define LOCK() EnterCriticalSection(&RecvLock) 39 # define UNLOCK() LeaveCriticalSection(&RecvLock) 40 #else 41 # define LOCK() do {} while (FALSE) 42 # define UNLOCK() do {} while (FALSE) 43 #endif 44 45 #ifdef DEBUG 46 static void uninit_recvbuff(void); 47 #endif 48 49 50 u_long 51 free_recvbuffs (void) 52 { 53 return free_recvbufs; 54 } 55 56 u_long 57 full_recvbuffs (void) 58 { 59 return full_recvbufs; 60 } 61 62 u_long 63 total_recvbuffs (void) 64 { 65 return total_recvbufs; 66 } 67 68 u_long 69 lowater_additions(void) 70 { 71 return lowater_adds; 72 } 73 74 static inline void 75 initialise_buffer(recvbuf_t *buff) 76 { 77 ZERO(*buff); 78 } 79 80 static void 81 create_buffers(int nbufs) 82 { 83 register recvbuf_t *bufp; 84 int i, abuf; 85 86 abuf = nbufs + buffer_shortfall; 87 buffer_shortfall = 0; 88 89 #ifndef DEBUG 90 bufp = emalloc_zero(abuf * sizeof(*bufp)); 91 #endif 92 93 for (i = 0; i < abuf; i++) { 94 #ifdef DEBUG 95 /* 96 * Allocate each buffer individually so they can be 97 * free()d during ntpd shutdown on DEBUG builds to 98 * keep them out of heap leak reports. 99 */ 100 bufp = emalloc_zero(sizeof(*bufp)); 101 #endif 102 LINK_SLIST(free_recv_list, bufp, link); 103 bufp++; 104 free_recvbufs++; 105 total_recvbufs++; 106 } 107 lowater_adds++; 108 } 109 110 void 111 init_recvbuff(int nbufs) 112 { 113 114 /* 115 * Init buffer free list and stat counters 116 */ 117 free_recvbufs = total_recvbufs = 0; 118 full_recvbufs = lowater_adds = 0; 119 120 create_buffers(nbufs); 121 122 #if defined(SYS_WINNT) 123 InitializeCriticalSection(&RecvLock); 124 #endif 125 126 #ifdef DEBUG 127 atexit(&uninit_recvbuff); 128 #endif 129 } 130 131 132 #ifdef DEBUG 133 static void 134 uninit_recvbuff(void) 135 { 136 recvbuf_t *rbunlinked; 137 138 for (;;) { 139 UNLINK_FIFO(rbunlinked, full_recv_fifo, link); 140 if (rbunlinked == NULL) 141 break; 142 free(rbunlinked); 143 } 144 145 for (;;) { 146 UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link); 147 if (rbunlinked == NULL) 148 break; 149 free(rbunlinked); 150 } 151 } 152 #endif /* DEBUG */ 153 154 155 /* 156 * freerecvbuf - make a single recvbuf available for reuse 157 */ 158 void 159 freerecvbuf(recvbuf_t *rb) 160 { 161 if (rb == NULL) { 162 msyslog(LOG_ERR, "freerecvbuff received NULL buffer"); 163 return; 164 } 165 166 LOCK(); 167 rb->used--; 168 if (rb->used != 0) 169 msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used); 170 LINK_SLIST(free_recv_list, rb, link); 171 free_recvbufs++; 172 UNLOCK(); 173 } 174 175 176 void 177 add_full_recv_buffer(recvbuf_t *rb) 178 { 179 if (rb == NULL) { 180 msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer"); 181 return; 182 } 183 LOCK(); 184 LINK_FIFO(full_recv_fifo, rb, link); 185 full_recvbufs++; 186 UNLOCK(); 187 } 188 189 190 recvbuf_t * 191 get_free_recv_buffer(void) 192 { 193 recvbuf_t *buffer; 194 195 LOCK(); 196 UNLINK_HEAD_SLIST(buffer, free_recv_list, link); 197 if (buffer != NULL) { 198 free_recvbufs--; 199 initialise_buffer(buffer); 200 buffer->used++; 201 } else { 202 buffer_shortfall++; 203 } 204 UNLOCK(); 205 206 return buffer; 207 } 208 209 210 #ifdef HAVE_IO_COMPLETION_PORT 211 recvbuf_t * 212 get_free_recv_buffer_alloc(void) 213 { 214 recvbuf_t *buffer; 215 216 buffer = get_free_recv_buffer(); 217 if (NULL == buffer) { 218 create_buffers(RECV_INC); 219 buffer = get_free_recv_buffer(); 220 } 221 ENSURE(buffer != NULL); 222 return (buffer); 223 } 224 #endif 225 226 227 recvbuf_t * 228 get_full_recv_buffer(void) 229 { 230 recvbuf_t * rbuf; 231 232 LOCK(); 233 234 #ifdef HAVE_SIGNALED_IO 235 /* 236 * make sure there are free buffers when we 237 * wander off to do lengthy packet processing with 238 * any buffer we grab from the full list. 239 * 240 * fixes malloc() interrupted by SIGIO risk 241 * (Bug 889) 242 */ 243 if (NULL == free_recv_list || buffer_shortfall > 0) { 244 /* 245 * try to get us some more buffers 246 */ 247 create_buffers(RECV_INC); 248 } 249 #endif 250 251 /* 252 * try to grab a full buffer 253 */ 254 UNLINK_FIFO(rbuf, full_recv_fifo, link); 255 if (rbuf != NULL) 256 full_recvbufs--; 257 UNLOCK(); 258 259 return rbuf; 260 } 261 262 263 /* 264 * purge_recv_buffers_for_fd() - purges any previously-received input 265 * from a given file descriptor. 266 */ 267 void 268 purge_recv_buffers_for_fd( 269 SOCKET fd 270 ) 271 { 272 recvbuf_t *rbufp; 273 recvbuf_t *next; 274 recvbuf_t *punlinked; 275 276 LOCK(); 277 278 for (rbufp = HEAD_FIFO(full_recv_fifo); 279 rbufp != NULL; 280 rbufp = next) { 281 next = rbufp->link; 282 if (rbufp->fd == fd) { 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 */ 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 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