xref: /netbsd/external/bsd/ntp/dist/lib/isc/mem.c (revision 9034ec65)
1 /*	$NetBSD: mem.c,v 1.5 2020/05/25 20:47:20 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2010, 2012  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 1997-2003  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id */
21 
22 /*! \file */
23 
24 #include <config.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 
30 #include <limits.h>
31 
32 #include <isc/magic.h>
33 #include <isc/mem.h>
34 #include <isc/msgs.h>
35 #include <isc/once.h>
36 #include <isc/ondestroy.h>
37 #include <isc/string.h>
38 #include <isc/mutex.h>
39 #include <isc/print.h>
40 #include <isc/util.h>
41 #include <isc/xml.h>
42 
43 #define MCTXLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) LOCK(l)
44 #define MCTXUNLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) UNLOCK(l)
45 
46 #ifndef ISC_MEM_DEBUGGING
47 #define ISC_MEM_DEBUGGING 0
48 #endif
49 LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
50 
51 /*
52  * Constants.
53  */
54 
55 #define DEF_MAX_SIZE		1100
56 #define DEF_MEM_TARGET		4096
57 #define ALIGNMENT_SIZE		8U		/*%< must be a power of 2 */
58 #define NUM_BASIC_BLOCKS	64		/*%< must be > 1 */
59 #define TABLE_INCREMENT		1024
60 #define DEBUGLIST_COUNT		1024
61 
62 /*
63  * Types.
64  */
65 typedef struct isc__mem isc__mem_t;
66 typedef struct isc__mempool isc__mempool_t;
67 
68 #if ISC_MEM_TRACKLINES
69 typedef struct debuglink debuglink_t;
70 struct debuglink {
71 	ISC_LINK(debuglink_t)	link;
72 	const void	       *ptr[DEBUGLIST_COUNT];
73 	unsigned int		size[DEBUGLIST_COUNT];
74 	const char	       *file[DEBUGLIST_COUNT];
75 	unsigned int		line[DEBUGLIST_COUNT];
76 	unsigned int		count;
77 };
78 
79 #define FLARG_PASS	, file, line
80 #define FLARG		, const char *file, unsigned int line
81 #else
82 #define FLARG_PASS
83 #define FLARG
84 #endif
85 
86 typedef struct element element;
87 struct element {
88 	element *		next;
89 };
90 
91 typedef struct {
92 	/*!
93 	 * This structure must be ALIGNMENT_SIZE bytes.
94 	 */
95 	union {
96 		size_t		size;
97 		isc__mem_t	*ctx;
98 		char		bytes[ALIGNMENT_SIZE];
99 	} u;
100 } size_info;
101 
102 struct stats {
103 	unsigned long		gets;
104 	unsigned long		totalgets;
105 	unsigned long		blocks;
106 	unsigned long		freefrags;
107 };
108 
109 #define MEM_MAGIC		ISC_MAGIC('M', 'e', 'm', 'C')
110 #define VALID_CONTEXT(c)	ISC_MAGIC_VALID(c, MEM_MAGIC)
111 
112 #if ISC_MEM_TRACKLINES
113 typedef ISC_LIST(debuglink_t)	debuglist_t;
114 #endif
115 
116 /* List of all active memory contexts. */
117 
118 static ISC_LIST(isc__mem_t)	contexts;
119 static isc_once_t		once = ISC_ONCE_INIT;
120 static isc_mutex_t		lock;
121 
122 /*%
123  * Total size of lost memory due to a bug of external library.
124  * Locked by the global lock.
125  */
126 static isc_uint64_t		totallost;
127 
128 struct isc__mem {
129 	isc_mem_t		common;
130 	isc_ondestroy_t		ondestroy;
131 	unsigned int		flags;
132 	isc_mutex_t		lock;
133 	isc_memalloc_t		memalloc;
134 	isc_memfree_t		memfree;
135 	void *			arg;
136 	size_t			max_size;
137 	isc_boolean_t		checkfree;
138 	struct stats *		stats;
139 	unsigned int		references;
140 	char			name[16];
141 	void *			tag;
142 	size_t			quota;
143 	size_t			total;
144 	size_t			inuse;
145 	size_t			maxinuse;
146 	size_t			hi_water;
147 	size_t			lo_water;
148 	isc_boolean_t		hi_called;
149 	isc_boolean_t		is_overmem;
150 	isc_mem_water_t		water;
151 	void *			water_arg;
152 	ISC_LIST(isc__mempool_t) pools;
153 	unsigned int		poolcnt;
154 
155 	/*  ISC_MEMFLAG_INTERNAL */
156 	size_t			mem_target;
157 	element **		freelists;
158 	element *		basic_blocks;
159 	unsigned char **	basic_table;
160 	unsigned int		basic_table_count;
161 	unsigned int		basic_table_size;
162 	unsigned char *		lowest;
163 	unsigned char *		highest;
164 
165 #if ISC_MEM_TRACKLINES
166 	debuglist_t *	 	debuglist;
167 	unsigned int		debuglistcnt;
168 #endif
169 
170 	unsigned int		memalloc_failures;
171 	ISC_LINK(isc__mem_t)	link;
172 };
173 
174 #define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
175 #define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
176 
177 struct isc__mempool {
178 	/* always unlocked */
179 	isc_mempool_t	common;		/*%< common header of mempool's */
180 	isc_mutex_t    *lock;		/*%< optional lock */
181 	isc__mem_t      *mctx;		/*%< our memory context */
182 	/*%< locked via the memory context's lock */
183 	ISC_LINK(isc__mempool_t)	link;	/*%< next pool in this mem context */
184 	/*%< optionally locked from here down */
185 	element	       *items;		/*%< low water item list */
186 	size_t		size;		/*%< size of each item on this pool */
187 	unsigned int	maxalloc;	/*%< max number of items allowed */
188 	unsigned int	allocated;	/*%< # of items currently given out */
189 	unsigned int	freecount;	/*%< # of items on reserved list */
190 	unsigned int	freemax;	/*%< # of items allowed on free list */
191 	unsigned int	fillcount;	/*%< # of items to fetch on each fill */
192 	/*%< Stats only. */
193 	unsigned int	gets;		/*%< # of requests to this pool */
194 	/*%< Debugging only. */
195 #if ISC_MEMPOOL_NAMES
196 	char		name[16];	/*%< printed name in stats reports */
197 #endif
198 };
199 
200 /*
201  * Private Inline-able.
202  */
203 
204 #if ! ISC_MEM_TRACKLINES
205 #define ADD_TRACE(a, b, c, d, e)
206 #define DELETE_TRACE(a, b, c, d, e)
207 #else
208 #define ADD_TRACE(a, b, c, d, e) \
209 	do { \
210 		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
211 					  ISC_MEM_DEBUGRECORD)) != 0 && \
212 		     b != NULL) \
213 			 add_trace_entry(a, b, c, d, e); \
214 	} while (0)
215 #define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)
216 
217 static void
218 print_active(isc__mem_t *ctx, FILE *out);
219 
220 /*%
221  * The following can be either static or public, depending on build environment.
222  */
223 
224 #ifdef BIND9
225 #define ISC_MEMFUNC_SCOPE
226 #else
227 #define ISC_MEMFUNC_SCOPE static
228 #endif
229 
230 ISC_MEMFUNC_SCOPE isc_result_t
231 isc__mem_createx(size_t init_max_size, size_t target_size,
232 		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
233 		 isc_mem_t **ctxp);
234 ISC_MEMFUNC_SCOPE isc_result_t
235 isc__mem_createx2(size_t init_max_size, size_t target_size,
236 		  isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
237 		  isc_mem_t **ctxp, unsigned int flags);
238 ISC_MEMFUNC_SCOPE isc_result_t
239 isc__mem_create(size_t init_max_size, size_t target_size, isc_mem_t **ctxp);
240 ISC_MEMFUNC_SCOPE isc_result_t
241 isc__mem_create2(size_t init_max_size, size_t target_size,
242 		 isc_mem_t **ctxp, unsigned int flags);
243 ISC_MEMFUNC_SCOPE void
244 isc__mem_attach(isc_mem_t *source, isc_mem_t **targetp);
245 ISC_MEMFUNC_SCOPE void
246 isc__mem_detach(isc_mem_t **ctxp);
247 ISC_MEMFUNC_SCOPE void
248 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG);
249 ISC_MEMFUNC_SCOPE void
250 isc__mem_destroy(isc_mem_t **ctxp);
251 ISC_MEMFUNC_SCOPE isc_result_t
252 isc__mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event);
253 ISC_MEMFUNC_SCOPE void *
254 isc___mem_get(isc_mem_t *ctx, size_t size FLARG);
255 ISC_MEMFUNC_SCOPE void
256 isc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG);
257 ISC_MEMFUNC_SCOPE void
258 isc__mem_stats(isc_mem_t *ctx, FILE *out);
259 ISC_MEMFUNC_SCOPE void *
260 isc___mem_allocate(isc_mem_t *ctx, size_t size FLARG);
261 ISC_MEMFUNC_SCOPE void *
262 isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG);
263 ISC_MEMFUNC_SCOPE void
264 isc___mem_free(isc_mem_t *ctx, void *ptr FLARG);
265 ISC_MEMFUNC_SCOPE char *
266 isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG);
267 ISC_MEMFUNC_SCOPE void
268 isc__mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag);
269 ISC_MEMFUNC_SCOPE void
270 isc__mem_setquota(isc_mem_t *ctx, size_t quota);
271 ISC_MEMFUNC_SCOPE size_t
272 isc__mem_getquota(isc_mem_t *ctx);
273 ISC_MEMFUNC_SCOPE size_t
274 isc__mem_inuse(isc_mem_t *ctx);
275 ISC_MEMFUNC_SCOPE isc_boolean_t
276 isc__mem_isovermem(isc_mem_t *ctx);
277 ISC_MEMFUNC_SCOPE void
278 isc__mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
279 		  size_t hiwater, size_t lowater);
280 ISC_MEMFUNC_SCOPE void
281 isc__mem_waterack(isc_mem_t *ctx0, int flag);
282 ISC_MEMFUNC_SCOPE void
283 isc__mem_setname(isc_mem_t *ctx, const char *name, void *tag);
284 ISC_MEMFUNC_SCOPE const char *
285 isc__mem_getname(isc_mem_t *ctx);
286 ISC_MEMFUNC_SCOPE void *
287 isc__mem_gettag(isc_mem_t *ctx);
288 ISC_MEMFUNC_SCOPE isc_result_t
289 isc__mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp);
290 ISC_MEMFUNC_SCOPE void
291 isc__mempool_setname(isc_mempool_t *mpctx, const char *name);
292 ISC_MEMFUNC_SCOPE void
293 isc__mempool_destroy(isc_mempool_t **mpctxp);
294 ISC_MEMFUNC_SCOPE void
295 isc__mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock);
296 ISC_MEMFUNC_SCOPE void *
297 isc___mempool_get(isc_mempool_t *mpctx FLARG);
298 ISC_MEMFUNC_SCOPE void
299 isc___mempool_put(isc_mempool_t *mpctx, void *mem FLARG);
300 ISC_MEMFUNC_SCOPE void
301 isc__mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit);
302 ISC_MEMFUNC_SCOPE unsigned int
303 isc__mempool_getfreemax(isc_mempool_t *mpctx);
304 ISC_MEMFUNC_SCOPE unsigned int
305 isc__mempool_getfreecount(isc_mempool_t *mpctx);
306 ISC_MEMFUNC_SCOPE void
307 isc__mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit);
308 ISC_MEMFUNC_SCOPE unsigned int
309 isc__mempool_getmaxalloc(isc_mempool_t *mpctx);
310 ISC_MEMFUNC_SCOPE unsigned int
311 isc__mempool_getallocated(isc_mempool_t *mpctx);
312 ISC_MEMFUNC_SCOPE void
313 isc__mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit);
314 ISC_MEMFUNC_SCOPE unsigned int
315 isc__mempool_getfillcount(isc_mempool_t *mpctx);
316 #ifdef BIND9
317 ISC_MEMFUNC_SCOPE void
318 isc__mem_printactive(isc_mem_t *ctx0, FILE *file);
319 ISC_MEMFUNC_SCOPE void
320 isc__mem_printallactive(FILE *file);
321 ISC_MEMFUNC_SCOPE void
322 isc__mem_checkdestroyed(FILE *file);
323 ISC_MEMFUNC_SCOPE unsigned int
324 isc__mem_references(isc_mem_t *ctx0);
325 #endif
326 
327 static struct isc__memmethods {
328 	isc_memmethods_t methods;
329 
330 	/*%
331 	 * The following are defined just for avoiding unused static functions.
332 	 */
333 #ifndef BIND9
334 	void *createx, *create, *create2, *ondestroy, *stats,
335 		*setquota, *getquota, *setname, *getname, *gettag;
336 #endif
337 } memmethods = {
338 	{
339 		isc__mem_attach,
340 		isc__mem_detach,
341 		isc__mem_destroy,
342 		isc___mem_get,
343 		isc___mem_put,
344 		isc___mem_putanddetach,
345 		isc___mem_allocate,
346 		isc___mem_reallocate,
347 		isc___mem_strdup,
348 		isc___mem_free,
349 		isc__mem_setdestroycheck,
350 		isc__mem_setwater,
351 		isc__mem_waterack,
352 		isc__mem_inuse,
353 		isc__mem_isovermem,
354 		isc__mempool_create
355 	}
356 #ifndef BIND9
357 	,
358 	(void *)isc__mem_createx, (void *)isc__mem_create,
359 	(void *)isc__mem_create2, (void *)isc__mem_ondestroy,
360 	(void *)isc__mem_stats, (void *)isc__mem_setquota,
361 	(void *)isc__mem_getquota, (void *)isc__mem_setname,
362 	(void *)isc__mem_getname, (void *)isc__mem_gettag
363 #endif
364 };
365 
366 static struct isc__mempoolmethods {
367 	isc_mempoolmethods_t methods;
368 
369 	/*%
370 	 * The following are defined just for avoiding unused static functions.
371 	 */
372 #ifndef BIND9
373 	void *getfreemax, *getfreecount, *getmaxalloc, *getfillcount;
374 #endif
375 } mempoolmethods = {
376 	{
377 		isc__mempool_destroy,
378 		isc___mempool_get,
379 		isc___mempool_put,
380 		isc__mempool_getallocated,
381 		isc__mempool_setmaxalloc,
382 		isc__mempool_setfreemax,
383 		isc__mempool_setname,
384 		isc__mempool_associatelock,
385 		isc__mempool_setfillcount
386 	}
387 #ifndef BIND9
388 	,
389 	(void *)isc__mempool_getfreemax, (void *)isc__mempool_getfreecount,
390 	(void *)isc__mempool_getmaxalloc, (void *)isc__mempool_getfillcount
391 #endif
392 };
393 
394 /*!
395  * mctx must be locked.
396  */
397 static inline void
add_trace_entry(isc__mem_t * mctx,const void * ptr,unsigned int size FLARG)398 add_trace_entry(isc__mem_t *mctx, const void *ptr, unsigned int size
399 		FLARG)
400 {
401 	debuglink_t *dl;
402 	unsigned int i;
403 	unsigned int mysize = size;
404 
405 	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
406 		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
407 					       ISC_MSG_ADDTRACE,
408 					       "add %p size %u "
409 					       "file %s line %u mctx %p\n"),
410 			ptr, size, file, line, mctx);
411 
412 	if (mctx->debuglist == NULL)
413 		return;
414 
415 	if (mysize > mctx->max_size)
416 		mysize = mctx->max_size;
417 
418 	dl = ISC_LIST_HEAD(mctx->debuglist[mysize]);
419 	while (dl != NULL) {
420 		if (dl->count == DEBUGLIST_COUNT)
421 			goto next;
422 		for (i = 0; i < DEBUGLIST_COUNT; i++) {
423 			if (dl->ptr[i] == NULL) {
424 				dl->ptr[i] = ptr;
425 				dl->size[i] = size;
426 				dl->file[i] = file;
427 				dl->line[i] = line;
428 				dl->count++;
429 				return;
430 			}
431 		}
432 	next:
433 		dl = ISC_LIST_NEXT(dl, link);
434 	}
435 
436 	dl = malloc(sizeof(debuglink_t));
437 	INSIST(dl != NULL);
438 
439 	ISC_LINK_INIT(dl, link);
440 	for (i = 1; i < DEBUGLIST_COUNT; i++) {
441 		dl->ptr[i] = NULL;
442 		dl->size[i] = 0;
443 		dl->file[i] = NULL;
444 		dl->line[i] = 0;
445 	}
446 
447 	dl->ptr[0] = ptr;
448 	dl->size[0] = size;
449 	dl->file[0] = file;
450 	dl->line[0] = line;
451 	dl->count = 1;
452 
453 	ISC_LIST_PREPEND(mctx->debuglist[mysize], dl, link);
454 	mctx->debuglistcnt++;
455 }
456 
457 static inline void
delete_trace_entry(isc__mem_t * mctx,const void * ptr,unsigned int size,const char * file,unsigned int line)458 delete_trace_entry(isc__mem_t *mctx, const void *ptr, unsigned int size,
459 		   const char *file, unsigned int line)
460 {
461 	debuglink_t *dl;
462 	unsigned int i;
463 
464 	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
465 		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
466 					       ISC_MSG_DELTRACE,
467 					       "del %p size %u "
468 					       "file %s line %u mctx %p\n"),
469 			ptr, size, file, line, mctx);
470 
471 	if (mctx->debuglist == NULL)
472 		return;
473 
474 	if (size > mctx->max_size)
475 		size = mctx->max_size;
476 
477 	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
478 	while (dl != NULL) {
479 		for (i = 0; i < DEBUGLIST_COUNT; i++) {
480 			if (dl->ptr[i] == ptr) {
481 				dl->ptr[i] = NULL;
482 				dl->size[i] = 0;
483 				dl->file[i] = NULL;
484 				dl->line[i] = 0;
485 
486 				INSIST(dl->count > 0);
487 				dl->count--;
488 				if (dl->count == 0) {
489 					ISC_LIST_UNLINK(mctx->debuglist[size],
490 							dl, link);
491 					free(dl);
492 				}
493 				return;
494 			}
495 		}
496 		dl = ISC_LIST_NEXT(dl, link);
497 	}
498 
499 	/*
500 	 * If we get here, we didn't find the item on the list.  We're
501 	 * screwed.
502 	 */
503 	INSIST(dl != NULL);
504 }
505 #endif /* ISC_MEM_TRACKLINES */
506 
507 static inline size_t
rmsize(size_t size)508 rmsize(size_t size) {
509 	/*
510 	 * round down to ALIGNMENT_SIZE
511 	 */
512 	return (size & (~(ALIGNMENT_SIZE - 1)));
513 }
514 
515 static inline size_t
quantize(size_t size)516 quantize(size_t size) {
517 	/*!
518 	 * Round up the result in order to get a size big
519 	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
520 	 * byte boundaries.
521 	 */
522 
523 	if (size == 0U)
524 		return (ALIGNMENT_SIZE);
525 	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
526 }
527 
528 static inline isc_boolean_t
more_basic_blocks(isc__mem_t * ctx)529 more_basic_blocks(isc__mem_t *ctx) {
530 	void *new;
531 	unsigned char *curr, *next;
532 	unsigned char *first, *last;
533 	unsigned char **table;
534 	unsigned int table_size;
535 	size_t increment;
536 	int i;
537 
538 	/* Require: we hold the context lock. */
539 
540 	/*
541 	 * Did we hit the quota for this context?
542 	 */
543 	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
544 	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
545 		return (ISC_FALSE);
546 
547 	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
548 	if (ctx->basic_table_count == ctx->basic_table_size) {
549 		table_size = ctx->basic_table_size + TABLE_INCREMENT;
550 		table = (ctx->memalloc)(ctx->arg,
551 					table_size * sizeof(unsigned char *));
552 		if (table == NULL) {
553 			ctx->memalloc_failures++;
554 			return (ISC_FALSE);
555 		}
556 		if (ctx->basic_table_size != 0) {
557 			memcpy(table, ctx->basic_table,
558 			       ctx->basic_table_size *
559 			       sizeof(unsigned char *));
560 			(ctx->memfree)(ctx->arg, ctx->basic_table);
561 		}
562 		ctx->basic_table = table;
563 		ctx->basic_table_size = table_size;
564 	}
565 
566 	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
567 	if (new == NULL) {
568 		ctx->memalloc_failures++;
569 		return (ISC_FALSE);
570 	}
571 	ctx->total += increment;
572 	ctx->basic_table[ctx->basic_table_count] = new;
573 	ctx->basic_table_count++;
574 
575 	curr = new;
576 	next = curr + ctx->mem_target;
577 	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
578 		((element *)curr)->next = (element *)next;
579 		curr = next;
580 		next += ctx->mem_target;
581 	}
582 	/*
583 	 * curr is now pointing at the last block in the
584 	 * array.
585 	 */
586 	((element *)curr)->next = NULL;
587 	first = new;
588 	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
589 	if (first < ctx->lowest || ctx->lowest == NULL)
590 		ctx->lowest = first;
591 	if (last > ctx->highest)
592 		ctx->highest = last;
593 	ctx->basic_blocks = new;
594 
595 	return (ISC_TRUE);
596 }
597 
598 static inline isc_boolean_t
more_frags(isc__mem_t * ctx,size_t new_size)599 more_frags(isc__mem_t *ctx, size_t new_size) {
600 	int i, frags;
601 	size_t total_size;
602 	void *new;
603 	unsigned char *curr, *next;
604 
605 	/*!
606 	 * Try to get more fragments by chopping up a basic block.
607 	 */
608 
609 	if (ctx->basic_blocks == NULL) {
610 		if (!more_basic_blocks(ctx)) {
611 			/*
612 			 * We can't get more memory from the OS, or we've
613 			 * hit the quota for this context.
614 			 */
615 			/*
616 			 * XXXRTH  "At quota" notification here.
617 			 */
618 			return (ISC_FALSE);
619 		}
620 	}
621 
622 	total_size = ctx->mem_target;
623 	new = ctx->basic_blocks;
624 	ctx->basic_blocks = ctx->basic_blocks->next;
625 	frags = total_size / new_size;
626 	ctx->stats[new_size].blocks++;
627 	ctx->stats[new_size].freefrags += frags;
628 	/*
629 	 * Set up a linked-list of blocks of size
630 	 * "new_size".
631 	 */
632 	curr = new;
633 	next = curr + new_size;
634 	total_size -= new_size;
635 	for (i = 0; i < (frags - 1); i++) {
636 		((element *)curr)->next = (element *)next;
637 		curr = next;
638 		next += new_size;
639 		total_size -= new_size;
640 	}
641 	/*
642 	 * Add the remaining fragment of the basic block to a free list.
643 	 */
644 	total_size = rmsize(total_size);
645 	if (total_size > 0U) {
646 		((element *)next)->next = ctx->freelists[total_size];
647 		ctx->freelists[total_size] = (element *)next;
648 		ctx->stats[total_size].freefrags++;
649 	}
650 	/*
651 	 * curr is now pointing at the last block in the
652 	 * array.
653 	 */
654 	((element *)curr)->next = NULL;
655 	ctx->freelists[new_size] = new;
656 
657 	return (ISC_TRUE);
658 }
659 
660 static inline void *
mem_getunlocked(isc__mem_t * ctx,size_t size)661 mem_getunlocked(isc__mem_t *ctx, size_t size) {
662 	size_t new_size = quantize(size);
663 	void *ret;
664 
665 	if (size >= ctx->max_size || new_size >= ctx->max_size) {
666 		/*
667 		 * memget() was called on something beyond our upper limit.
668 		 */
669 		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
670 			ret = NULL;
671 			goto done;
672 		}
673 		ret = (ctx->memalloc)(ctx->arg, size);
674 		if (ret == NULL) {
675 			ctx->memalloc_failures++;
676 			goto done;
677 		}
678 		ctx->total += size;
679 		ctx->inuse += size;
680 		ctx->stats[ctx->max_size].gets++;
681 		ctx->stats[ctx->max_size].totalgets++;
682 		/*
683 		 * If we don't set new_size to size, then the
684 		 * ISC_MEM_FILL code might write over bytes we
685 		 * don't own.
686 		 */
687 		new_size = size;
688 		goto done;
689 	}
690 
691 	/*
692 	 * If there are no blocks in the free list for this size, get a chunk
693 	 * of memory and then break it up into "new_size"-sized blocks, adding
694 	 * them to the free list.
695 	 */
696 	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
697 		return (NULL);
698 
699 	/*
700 	 * The free list uses the "rounded-up" size "new_size".
701 	 */
702 	ret = ctx->freelists[new_size];
703 	ctx->freelists[new_size] = ctx->freelists[new_size]->next;
704 
705 	/*
706 	 * The stats[] uses the _actual_ "size" requested by the
707 	 * caller, with the caveat (in the code above) that "size" >= the
708 	 * max. size (max_size) ends up getting recorded as a call to
709 	 * max_size.
710 	 */
711 	ctx->stats[size].gets++;
712 	ctx->stats[size].totalgets++;
713 	ctx->stats[new_size].freefrags--;
714 	ctx->inuse += new_size;
715 
716  done:
717 
718 #if ISC_MEM_FILL
719 	if (ret != NULL)
720 		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
721 #endif
722 
723 	return (ret);
724 }
725 
726 #if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
727 static inline void
check_overrun(void * mem,size_t size,size_t new_size)728 check_overrun(void *mem, size_t size, size_t new_size) {
729 	unsigned char *cp;
730 
731 	cp = (unsigned char *)mem;
732 	cp += size;
733 	while (size < new_size) {
734 		INSIST(*cp == 0xbe);
735 		cp++;
736 		size++;
737 	}
738 }
739 #endif
740 
741 static inline void
mem_putunlocked(isc__mem_t * ctx,void * mem,size_t size)742 mem_putunlocked(isc__mem_t *ctx, void *mem, size_t size) {
743 	size_t new_size = quantize(size);
744 
745 	if (size == ctx->max_size || new_size >= ctx->max_size) {
746 		/*
747 		 * memput() called on something beyond our upper limit.
748 		 */
749 #if ISC_MEM_FILL
750 		memset(mem, 0xde, size); /* Mnemonic for "dead". */
751 #endif
752 		(ctx->memfree)(ctx->arg, mem);
753 		INSIST(ctx->stats[ctx->max_size].gets != 0U);
754 		ctx->stats[ctx->max_size].gets--;
755 		INSIST(size <= ctx->total);
756 		ctx->inuse -= size;
757 		ctx->total -= size;
758 		return;
759 	}
760 
761 #if ISC_MEM_FILL
762 #if ISC_MEM_CHECKOVERRUN
763 	check_overrun(mem, size, new_size);
764 #endif
765 	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
766 #endif
767 
768 	/*
769 	 * The free list uses the "rounded-up" size "new_size".
770 	 */
771 	((element *)mem)->next = ctx->freelists[new_size];
772 	ctx->freelists[new_size] = (element *)mem;
773 
774 	/*
775 	 * The stats[] uses the _actual_ "size" requested by the
776 	 * caller, with the caveat (in the code above) that "size" >= the
777 	 * max. size (max_size) ends up getting recorded as a call to
778 	 * max_size.
779 	 */
780 	INSIST(ctx->stats[size].gets != 0U);
781 	ctx->stats[size].gets--;
782 	ctx->stats[new_size].freefrags++;
783 	ctx->inuse -= new_size;
784 }
785 
786 /*!
787  * Perform a malloc, doing memory filling and overrun detection as necessary.
788  */
789 static inline void *
mem_get(isc__mem_t * ctx,size_t size)790 mem_get(isc__mem_t *ctx, size_t size) {
791 	char *ret;
792 
793 #if ISC_MEM_CHECKOVERRUN
794 	size += 1;
795 #endif
796 
797 	ret = (ctx->memalloc)(ctx->arg, size);
798 	if (ret == NULL)
799 		ctx->memalloc_failures++;
800 
801 #if ISC_MEM_FILL
802 	if (ret != NULL)
803 		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
804 #else
805 #  if ISC_MEM_CHECKOVERRUN
806 	if (ret != NULL)
807 		ret[size-1] = 0xbe;
808 #  endif
809 #endif
810 
811 	return (ret);
812 }
813 
814 /*!
815  * Perform a free, doing memory filling and overrun detection as necessary.
816  */
817 static inline void
mem_put(isc__mem_t * ctx,void * mem,size_t size)818 mem_put(isc__mem_t *ctx, void *mem, size_t size) {
819 #if ISC_MEM_CHECKOVERRUN
820 	INSIST(((unsigned char *)mem)[size] == 0xbe);
821 #endif
822 #if ISC_MEM_FILL
823 	memset(mem, 0xde, size); /* Mnemonic for "dead". */
824 #else
825 	UNUSED(size);
826 #endif
827 	(ctx->memfree)(ctx->arg, mem);
828 }
829 
830 /*!
831  * Update internal counters after a memory get.
832  */
833 static inline void
mem_getstats(isc__mem_t * ctx,size_t size)834 mem_getstats(isc__mem_t *ctx, size_t size) {
835 	ctx->total += size;
836 	ctx->inuse += size;
837 
838 	if (size > ctx->max_size) {
839 		ctx->stats[ctx->max_size].gets++;
840 		ctx->stats[ctx->max_size].totalgets++;
841 	} else {
842 		ctx->stats[size].gets++;
843 		ctx->stats[size].totalgets++;
844 	}
845 }
846 
847 /*!
848  * Update internal counters after a memory put.
849  */
850 static inline void
mem_putstats(isc__mem_t * ctx,void * ptr,size_t size)851 mem_putstats(isc__mem_t *ctx, void *ptr, size_t size) {
852 	UNUSED(ptr);
853 
854 	INSIST(ctx->inuse >= size);
855 	ctx->inuse -= size;
856 
857 	if (size > ctx->max_size) {
858 		INSIST(ctx->stats[ctx->max_size].gets > 0U);
859 		ctx->stats[ctx->max_size].gets--;
860 	} else {
861 		INSIST(ctx->stats[size].gets > 0U);
862 		ctx->stats[size].gets--;
863 	}
864 }
865 
866 /*
867  * Private.
868  */
869 
870 static void *
default_memalloc(void * arg,size_t size)871 default_memalloc(void *arg, size_t size) {
872 	UNUSED(arg);
873 	if (size == 0U)
874 		size = 1;
875 	return (malloc(size));
876 }
877 
878 static void
default_memfree(void * arg,void * ptr)879 default_memfree(void *arg, void *ptr) {
880 	UNUSED(arg);
881 	free(ptr);
882 }
883 
884 static void
initialize_action(void)885 initialize_action(void) {
886 	RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
887 	ISC_LIST_INIT(contexts);
888 	totallost = 0;
889 }
890 
891 /*
892  * Public.
893  */
894 
895 ISC_MEMFUNC_SCOPE isc_result_t
isc__mem_createx(size_t init_max_size,size_t target_size,isc_memalloc_t memalloc,isc_memfree_t memfree,void * arg,isc_mem_t ** ctxp)896 isc__mem_createx(size_t init_max_size, size_t target_size,
897 		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
898 		 isc_mem_t **ctxp)
899 {
900 	return (isc__mem_createx2(init_max_size, target_size, memalloc, memfree,
901 				  arg, ctxp, ISC_MEMFLAG_DEFAULT));
902 
903 }
904 
905 ISC_MEMFUNC_SCOPE isc_result_t
isc__mem_createx2(size_t init_max_size,size_t target_size,isc_memalloc_t memalloc,isc_memfree_t memfree,void * arg,isc_mem_t ** ctxp,unsigned int flags)906 isc__mem_createx2(size_t init_max_size, size_t target_size,
907 		  isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
908 		  isc_mem_t **ctxp, unsigned int flags)
909 {
910 	isc__mem_t *ctx;
911 	isc_result_t result;
912 
913 	REQUIRE(ctxp != NULL && *ctxp == NULL);
914 	REQUIRE(memalloc != NULL);
915 	REQUIRE(memfree != NULL);
916 
917 	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);
918 
919 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
920 
921 	ctx = (memalloc)(arg, sizeof(*ctx));
922 	if (ctx == NULL)
923 		return (ISC_R_NOMEMORY);
924 
925 	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
926 		result = isc_mutex_init(&ctx->lock);
927 		if (result != ISC_R_SUCCESS) {
928 			(memfree)(arg, ctx);
929 			return (result);
930 		}
931 	}
932 
933 	if (init_max_size == 0U)
934 		ctx->max_size = DEF_MAX_SIZE;
935 	else
936 		ctx->max_size = init_max_size;
937 	ctx->flags = flags;
938 	ctx->references = 1;
939 	memset(ctx->name, 0, sizeof(ctx->name));
940 	ctx->tag = NULL;
941 	ctx->quota = 0;
942 	ctx->total = 0;
943 	ctx->inuse = 0;
944 	ctx->maxinuse = 0;
945 	ctx->hi_water = 0;
946 	ctx->lo_water = 0;
947 	ctx->hi_called = ISC_FALSE;
948 	ctx->is_overmem = ISC_FALSE;
949 	ctx->water = NULL;
950 	ctx->water_arg = NULL;
951 	ctx->common.impmagic = MEM_MAGIC;
952 	ctx->common.magic = ISCAPI_MCTX_MAGIC;
953 	ctx->common.methods = (isc_memmethods_t *)&memmethods;
954 	isc_ondestroy_init(&ctx->ondestroy);
955 	ctx->memalloc = memalloc;
956 	ctx->memfree = memfree;
957 	ctx->arg = arg;
958 	ctx->stats = NULL;
959 	ctx->checkfree = ISC_TRUE;
960 #if ISC_MEM_TRACKLINES
961 	ctx->debuglist = NULL;
962 	ctx->debuglistcnt = 0;
963 #endif
964 	ISC_LIST_INIT(ctx->pools);
965 	ctx->poolcnt = 0;
966 	ctx->freelists = NULL;
967 	ctx->basic_blocks = NULL;
968 	ctx->basic_table = NULL;
969 	ctx->basic_table_count = 0;
970 	ctx->basic_table_size = 0;
971 	ctx->lowest = NULL;
972 	ctx->highest = NULL;
973 
974 	ctx->stats = (memalloc)(arg,
975 				(ctx->max_size+1) * sizeof(struct stats));
976 	if (ctx->stats == NULL) {
977 		result = ISC_R_NOMEMORY;
978 		goto error;
979 	}
980 	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
981 
982 	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
983 		if (target_size == 0U)
984 			ctx->mem_target = DEF_MEM_TARGET;
985 		else
986 			ctx->mem_target = target_size;
987 		ctx->freelists = (memalloc)(arg, ctx->max_size *
988 						 sizeof(element *));
989 		if (ctx->freelists == NULL) {
990 			result = ISC_R_NOMEMORY;
991 			goto error;
992 		}
993 		memset(ctx->freelists, 0,
994 		       ctx->max_size * sizeof(element *));
995 	}
996 
997 #if ISC_MEM_TRACKLINES
998 	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
999 		unsigned int i;
1000 
1001 		ctx->debuglist = (memalloc)(arg,
1002 				      (ctx->max_size+1) * sizeof(debuglist_t));
1003 		if (ctx->debuglist == NULL) {
1004 			result = ISC_R_NOMEMORY;
1005 			goto error;
1006 		}
1007 		for (i = 0; i <= ctx->max_size; i++)
1008 			ISC_LIST_INIT(ctx->debuglist[i]);
1009 	}
1010 #endif
1011 
1012 	ctx->memalloc_failures = 0;
1013 
1014 	LOCK(&lock);
1015 	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
1016 	UNLOCK(&lock);
1017 
1018 	*ctxp = (isc_mem_t *)ctx;
1019 	return (ISC_R_SUCCESS);
1020 
1021   error:
1022 	if (ctx != NULL) {
1023 		if (ctx->stats != NULL)
1024 			(memfree)(arg, ctx->stats);
1025 		if (ctx->freelists != NULL)
1026 			(memfree)(arg, ctx->freelists);
1027 #if ISC_MEM_TRACKLINES
1028 		if (ctx->debuglist != NULL)
1029 			(ctx->memfree)(ctx->arg, ctx->debuglist);
1030 #endif /* ISC_MEM_TRACKLINES */
1031 		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
1032 			DESTROYLOCK(&ctx->lock);
1033 		(memfree)(arg, ctx);
1034 	}
1035 
1036 	return (result);
1037 }
1038 
1039 ISC_MEMFUNC_SCOPE isc_result_t
isc__mem_create(size_t init_max_size,size_t target_size,isc_mem_t ** ctxp)1040 isc__mem_create(size_t init_max_size, size_t target_size, isc_mem_t **ctxp) {
1041 	return (isc__mem_createx2(init_max_size, target_size,
1042 				  default_memalloc, default_memfree, NULL,
1043 				  ctxp, ISC_MEMFLAG_DEFAULT));
1044 }
1045 
1046 ISC_MEMFUNC_SCOPE isc_result_t
isc__mem_create2(size_t init_max_size,size_t target_size,isc_mem_t ** ctxp,unsigned int flags)1047 isc__mem_create2(size_t init_max_size, size_t target_size,
1048 		 isc_mem_t **ctxp, unsigned int flags)
1049 {
1050 	return (isc__mem_createx2(init_max_size, target_size,
1051 				  default_memalloc, default_memfree, NULL,
1052 				  ctxp, flags));
1053 }
1054 
1055 static void
destroy(isc__mem_t * ctx)1056 destroy(isc__mem_t *ctx) {
1057 	unsigned int i;
1058 	isc_ondestroy_t ondest;
1059 
1060 	LOCK(&lock);
1061 	ISC_LIST_UNLINK(contexts, ctx, link);
1062 	totallost += ctx->inuse;
1063 	UNLOCK(&lock);
1064 
1065 	ctx->common.impmagic = 0;
1066 	ctx->common.magic = 0;
1067 
1068 	INSIST(ISC_LIST_EMPTY(ctx->pools));
1069 
1070 #if ISC_MEM_TRACKLINES
1071 	if (ctx->debuglist != NULL) {
1072 		if (ctx->checkfree) {
1073 			for (i = 0; i <= ctx->max_size; i++) {
1074 				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
1075 					print_active(ctx, stderr);
1076 				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
1077 			}
1078 		} else {
1079 			debuglink_t *dl;
1080 
1081 			for (i = 0; i <= ctx->max_size; i++)
1082 				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
1083 				     dl != NULL;
1084 				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
1085 					ISC_LIST_UNLINK(ctx->debuglist[i],
1086 							dl, link);
1087 					free(dl);
1088 				}
1089 		}
1090 		(ctx->memfree)(ctx->arg, ctx->debuglist);
1091 	}
1092 #endif
1093 	INSIST(ctx->references == 0);
1094 
1095 	if (ctx->checkfree) {
1096 		for (i = 0; i <= ctx->max_size; i++) {
1097 #if ISC_MEM_TRACKLINES
1098 			if (ctx->stats[i].gets != 0U)
1099 				print_active(ctx, stderr);
1100 #endif
1101 			INSIST(ctx->stats[i].gets == 0U);
1102 		}
1103 	}
1104 
1105 	(ctx->memfree)(ctx->arg, ctx->stats);
1106 
1107 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1108 		for (i = 0; i < ctx->basic_table_count; i++)
1109 			(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
1110 		(ctx->memfree)(ctx->arg, ctx->freelists);
1111 		if (ctx->basic_table != NULL)
1112 			(ctx->memfree)(ctx->arg, ctx->basic_table);
1113 	}
1114 
1115 	ondest = ctx->ondestroy;
1116 
1117 	if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
1118 		DESTROYLOCK(&ctx->lock);
1119 	(ctx->memfree)(ctx->arg, ctx);
1120 
1121 	isc_ondestroy_notify(&ondest, ctx);
1122 }
1123 
1124 ISC_MEMFUNC_SCOPE void
isc__mem_attach(isc_mem_t * source0,isc_mem_t ** targetp)1125 isc__mem_attach(isc_mem_t *source0, isc_mem_t **targetp) {
1126 	isc__mem_t *source = (isc__mem_t *)source0;
1127 
1128 	REQUIRE(VALID_CONTEXT(source));
1129 	REQUIRE(targetp != NULL && *targetp == NULL);
1130 
1131 	MCTXLOCK(source, &source->lock);
1132 	source->references++;
1133 	MCTXUNLOCK(source, &source->lock);
1134 
1135 	*targetp = (isc_mem_t *)source;
1136 }
1137 
1138 ISC_MEMFUNC_SCOPE void
isc__mem_detach(isc_mem_t ** ctxp)1139 isc__mem_detach(isc_mem_t **ctxp) {
1140 	isc__mem_t *ctx;
1141 	isc_boolean_t want_destroy = ISC_FALSE;
1142 
1143 	REQUIRE(ctxp != NULL);
1144 	ctx = (isc__mem_t *)*ctxp;
1145 	REQUIRE(VALID_CONTEXT(ctx));
1146 
1147 	MCTXLOCK(ctx, &ctx->lock);
1148 	INSIST(ctx->references > 0);
1149 	ctx->references--;
1150 	if (ctx->references == 0)
1151 		want_destroy = ISC_TRUE;
1152 	MCTXUNLOCK(ctx, &ctx->lock);
1153 
1154 	if (want_destroy)
1155 		destroy(ctx);
1156 
1157 	*ctxp = NULL;
1158 }
1159 
1160 /*
1161  * isc_mem_putanddetach() is the equivalent of:
1162  *
1163  * mctx = NULL;
1164  * isc_mem_attach(ptr->mctx, &mctx);
1165  * isc_mem_detach(&ptr->mctx);
1166  * isc_mem_put(mctx, ptr, sizeof(*ptr);
1167  * isc_mem_detach(&mctx);
1168  */
1169 
1170 ISC_MEMFUNC_SCOPE void
isc___mem_putanddetach(isc_mem_t ** ctxp,void * ptr,size_t size FLARG)1171 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
1172 	isc__mem_t *ctx;
1173 	isc_boolean_t want_destroy = ISC_FALSE;
1174 	size_info *si;
1175 	size_t oldsize;
1176 
1177 	REQUIRE(ctxp != NULL);
1178 	ctx = (isc__mem_t *)*ctxp;
1179 	REQUIRE(VALID_CONTEXT(ctx));
1180 	REQUIRE(ptr != NULL);
1181 
1182 	/*
1183 	 * Must be before mem_putunlocked() as ctxp is usually within
1184 	 * [ptr..ptr+size).
1185 	 */
1186 	*ctxp = NULL;
1187 
1188 	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1189 		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1190 			si = &(((size_info *)ptr)[-1]);
1191 			oldsize = si->u.size - ALIGNMENT_SIZE;
1192 			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1193 				oldsize -= ALIGNMENT_SIZE;
1194 			INSIST(oldsize == size);
1195 		}
1196 		isc_mem_free((isc_mem_t *)ctx, ptr);
1197 
1198 		MCTXLOCK(ctx, &ctx->lock);
1199 		ctx->references--;
1200 		if (ctx->references == 0)
1201 			want_destroy = ISC_TRUE;
1202 		MCTXUNLOCK(ctx, &ctx->lock);
1203 		if (want_destroy)
1204 			destroy(ctx);
1205 
1206 		return;
1207 	}
1208 
1209 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1210 		MCTXLOCK(ctx, &ctx->lock);
1211 		mem_putunlocked(ctx, ptr, size);
1212 	} else {
1213 		mem_put(ctx, ptr, size);
1214 		MCTXLOCK(ctx, &ctx->lock);
1215 		mem_putstats(ctx, ptr, size);
1216 	}
1217 
1218 	DELETE_TRACE(ctx, ptr, size, file, line);
1219 	INSIST(ctx->references > 0);
1220 	ctx->references--;
1221 	if (ctx->references == 0)
1222 		want_destroy = ISC_TRUE;
1223 
1224 	MCTXUNLOCK(ctx, &ctx->lock);
1225 
1226 	if (want_destroy)
1227 		destroy(ctx);
1228 }
1229 
1230 ISC_MEMFUNC_SCOPE void
isc__mem_destroy(isc_mem_t ** ctxp)1231 isc__mem_destroy(isc_mem_t **ctxp) {
1232 	isc__mem_t *ctx;
1233 
1234 	/*
1235 	 * This routine provides legacy support for callers who use mctxs
1236 	 * without attaching/detaching.
1237 	 */
1238 
1239 	REQUIRE(ctxp != NULL);
1240 	ctx = (isc__mem_t *)*ctxp;
1241 	REQUIRE(VALID_CONTEXT(ctx));
1242 
1243 	MCTXLOCK(ctx, &ctx->lock);
1244 #if ISC_MEM_TRACKLINES
1245 	if (ctx->references != 1)
1246 		print_active(ctx, stderr);
1247 #endif
1248 	REQUIRE(ctx->references == 1);
1249 	ctx->references--;
1250 	MCTXUNLOCK(ctx, &ctx->lock);
1251 
1252 	destroy(ctx);
1253 
1254 	*ctxp = NULL;
1255 }
1256 
1257 ISC_MEMFUNC_SCOPE isc_result_t
isc__mem_ondestroy(isc_mem_t * ctx0,isc_task_t * task,isc_event_t ** event)1258 isc__mem_ondestroy(isc_mem_t *ctx0, isc_task_t *task, isc_event_t **event) {
1259 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1260 	isc_result_t res;
1261 
1262 	MCTXLOCK(ctx, &ctx->lock);
1263 	res = isc_ondestroy_register(&ctx->ondestroy, task, event);
1264 	MCTXUNLOCK(ctx, &ctx->lock);
1265 
1266 	return (res);
1267 }
1268 
1269 ISC_MEMFUNC_SCOPE void *
isc___mem_get(isc_mem_t * ctx0,size_t size FLARG)1270 isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
1271 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1272 	void *ptr;
1273 	isc_boolean_t call_water = ISC_FALSE;
1274 
1275 	REQUIRE(VALID_CONTEXT(ctx));
1276 
1277 	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
1278 		return (isc__mem_allocate(ctx0, size FLARG_PASS));
1279 
1280 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1281 		MCTXLOCK(ctx, &ctx->lock);
1282 		ptr = mem_getunlocked(ctx, size);
1283 	} else {
1284 		ptr = mem_get(ctx, size);
1285 		MCTXLOCK(ctx, &ctx->lock);
1286 		if (ptr != NULL)
1287 			mem_getstats(ctx, size);
1288 	}
1289 
1290 	ADD_TRACE(ctx, ptr, size, file, line);
1291 	if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1292 	    !ctx->is_overmem) {
1293 		ctx->is_overmem = ISC_TRUE;
1294 	}
1295 	if (ctx->hi_water != 0U && !ctx->hi_called &&
1296 	    ctx->inuse > ctx->hi_water) {
1297 		call_water = ISC_TRUE;
1298 	}
1299 	if (ctx->inuse > ctx->maxinuse) {
1300 		ctx->maxinuse = ctx->inuse;
1301 		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1302 		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1303 			fprintf(stderr, "maxinuse = %lu\n",
1304 				(unsigned long)ctx->inuse);
1305 	}
1306 	MCTXUNLOCK(ctx, &ctx->lock);
1307 
1308 	if (call_water)
1309 		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1310 
1311 	return (ptr);
1312 }
1313 
1314 ISC_MEMFUNC_SCOPE void
isc___mem_put(isc_mem_t * ctx0,void * ptr,size_t size FLARG)1315 isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1316 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1317 	isc_boolean_t call_water = ISC_FALSE;
1318 	size_info *si;
1319 	size_t oldsize;
1320 
1321 	REQUIRE(VALID_CONTEXT(ctx));
1322 	REQUIRE(ptr != NULL);
1323 
1324 	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1325 		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1326 			si = &(((size_info *)ptr)[-1]);
1327 			oldsize = si->u.size - ALIGNMENT_SIZE;
1328 			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1329 				oldsize -= ALIGNMENT_SIZE;
1330 			INSIST(oldsize == size);
1331 		}
1332 		isc_mem_free((isc_mem_t *)ctx, ptr);
1333 		return;
1334 	}
1335 
1336 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1337 		MCTXLOCK(ctx, &ctx->lock);
1338 		mem_putunlocked(ctx, ptr, size);
1339 	} else {
1340 		mem_put(ctx, ptr, size);
1341 		MCTXLOCK(ctx, &ctx->lock);
1342 		mem_putstats(ctx, ptr, size);
1343 	}
1344 
1345 	DELETE_TRACE(ctx, ptr, size, file, line);
1346 
1347 	/*
1348 	 * The check against ctx->lo_water == 0 is for the condition
1349 	 * when the context was pushed over hi_water but then had
1350 	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1351 	 */
1352 	if (ctx->is_overmem &&
1353 	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1354 		ctx->is_overmem = ISC_FALSE;
1355 	}
1356 	if (ctx->hi_called &&
1357 	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1358 		if (ctx->water != NULL)
1359 			call_water = ISC_TRUE;
1360 	}
1361 	MCTXUNLOCK(ctx, &ctx->lock);
1362 
1363 	if (call_water)
1364 		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1365 }
1366 
1367 ISC_MEMFUNC_SCOPE void
isc__mem_waterack(isc_mem_t * ctx0,int flag)1368 isc__mem_waterack(isc_mem_t *ctx0, int flag) {
1369 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1370 
1371 	REQUIRE(VALID_CONTEXT(ctx));
1372 
1373 	MCTXLOCK(ctx, &ctx->lock);
1374 	if (flag == ISC_MEM_LOWATER)
1375 		ctx->hi_called = ISC_FALSE;
1376 	else if (flag == ISC_MEM_HIWATER)
1377 		ctx->hi_called = ISC_TRUE;
1378 	MCTXUNLOCK(ctx, &ctx->lock);
1379 }
1380 
1381 #if ISC_MEM_TRACKLINES
1382 static void
print_active(isc__mem_t * mctx,FILE * out)1383 print_active(isc__mem_t *mctx, FILE *out) {
1384 	if (mctx->debuglist != NULL) {
1385 		debuglink_t *dl;
1386 		unsigned int i, j;
1387 		const char *format;
1388 		isc_boolean_t found;
1389 
1390 		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1391 					    ISC_MSG_DUMPALLOC,
1392 					    "Dump of all outstanding "
1393 					    "memory allocations:\n"));
1394 		found = ISC_FALSE;
1395 		format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1396 					ISC_MSG_PTRFILELINE,
1397 					"\tptr %p size %u file %s line %u\n");
1398 		for (i = 0; i <= mctx->max_size; i++) {
1399 			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
1400 
1401 			if (dl != NULL)
1402 				found = ISC_TRUE;
1403 
1404 			while (dl != NULL) {
1405 				for (j = 0; j < DEBUGLIST_COUNT; j++)
1406 					if (dl->ptr[j] != NULL)
1407 						fprintf(out, format,
1408 							dl->ptr[j],
1409 							dl->size[j],
1410 							dl->file[j],
1411 							dl->line[j]);
1412 				dl = ISC_LIST_NEXT(dl, link);
1413 			}
1414 		}
1415 		if (!found)
1416 			fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1417 						    ISC_MSG_NONE, "\tNone.\n"));
1418 	}
1419 }
1420 #endif
1421 
1422 /*
1423  * Print the stats[] on the stream "out" with suitable formatting.
1424  */
1425 ISC_MEMFUNC_SCOPE void
isc__mem_stats(isc_mem_t * ctx0,FILE * out)1426 isc__mem_stats(isc_mem_t *ctx0, FILE *out) {
1427 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1428 	size_t i;
1429 	const struct stats *s;
1430 	const isc__mempool_t *pool;
1431 
1432 	REQUIRE(VALID_CONTEXT(ctx));
1433 	MCTXLOCK(ctx, &ctx->lock);
1434 
1435 	for (i = 0; i <= ctx->max_size; i++) {
1436 		s = &ctx->stats[i];
1437 
1438 		if (s->totalgets == 0U && s->gets == 0U)
1439 			continue;
1440 		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
1441 			(i == ctx->max_size) ? ">=" : "  ",
1442 			(unsigned long) i, s->totalgets, s->gets);
1443 		if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
1444 		    (s->blocks != 0U || s->freefrags != 0U))
1445 			fprintf(out, " (%lu bl, %lu ff)",
1446 				s->blocks, s->freefrags);
1447 		fputc('\n', out);
1448 	}
1449 
1450 	/*
1451 	 * Note that since a pool can be locked now, these stats might be
1452 	 * somewhat off if the pool is in active use at the time the stats
1453 	 * are dumped.  The link fields are protected by the isc_mem_t's
1454 	 * lock, however, so walking this list and extracting integers from
1455 	 * stats fields is always safe.
1456 	 */
1457 	pool = ISC_LIST_HEAD(ctx->pools);
1458 	if (pool != NULL) {
1459 		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1460 					    ISC_MSG_POOLSTATS,
1461 					    "[Pool statistics]\n"));
1462 		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
1463 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1464 				       ISC_MSG_POOLNAME, "name"),
1465 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1466 				       ISC_MSG_POOLSIZE, "size"),
1467 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1468 				       ISC_MSG_POOLMAXALLOC, "maxalloc"),
1469 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1470 				       ISC_MSG_POOLALLOCATED, "allocated"),
1471 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1472 				       ISC_MSG_POOLFREECOUNT, "freecount"),
1473 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1474 				       ISC_MSG_POOLFREEMAX, "freemax"),
1475 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1476 				       ISC_MSG_POOLFILLCOUNT, "fillcount"),
1477 			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1478 				       ISC_MSG_POOLGETS, "gets"),
1479 			"L");
1480 	}
1481 	while (pool != NULL) {
1482 		fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
1483 			pool->name, (unsigned long) pool->size, pool->maxalloc,
1484 			pool->allocated, pool->freecount, pool->freemax,
1485 			pool->fillcount, pool->gets,
1486 			(pool->lock == NULL ? "N" : "Y"));
1487 		pool = ISC_LIST_NEXT(pool, link);
1488 	}
1489 
1490 #if ISC_MEM_TRACKLINES
1491 	print_active(ctx, out);
1492 #endif
1493 
1494 	MCTXUNLOCK(ctx, &ctx->lock);
1495 }
1496 
1497 /*
1498  * Replacements for malloc() and free() -- they implicitly remember the
1499  * size of the object allocated (with some additional overhead).
1500  */
1501 
1502 static void *
isc__mem_allocateunlocked(isc_mem_t * ctx0,size_t size)1503 isc__mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
1504 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1505 	size_info *si;
1506 
1507 	size += ALIGNMENT_SIZE;
1508 	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1509 		size += ALIGNMENT_SIZE;
1510 
1511 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
1512 		si = mem_getunlocked(ctx, size);
1513 	else
1514 		si = mem_get(ctx, size);
1515 
1516 	if (si == NULL)
1517 		return (NULL);
1518 	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1519 		si->u.ctx = ctx;
1520 		si++;
1521 	}
1522 	si->u.size = size;
1523 	return (&si[1]);
1524 }
1525 
1526 ISC_MEMFUNC_SCOPE void *
isc___mem_allocate(isc_mem_t * ctx0,size_t size FLARG)1527 isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) {
1528 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1529 	size_info *si;
1530 	isc_boolean_t call_water = ISC_FALSE;
1531 
1532 	REQUIRE(VALID_CONTEXT(ctx));
1533 
1534 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1535 		MCTXLOCK(ctx, &ctx->lock);
1536 		si = isc__mem_allocateunlocked((isc_mem_t *)ctx, size);
1537 	} else {
1538 		si = isc__mem_allocateunlocked((isc_mem_t *)ctx, size);
1539 		MCTXLOCK(ctx, &ctx->lock);
1540 		if (si != NULL)
1541 			mem_getstats(ctx, si[-1].u.size);
1542 	}
1543 
1544 #if ISC_MEM_TRACKLINES
1545 	ADD_TRACE(ctx, si, si[-1].u.size, file, line);
1546 #endif
1547 	if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1548 	    !ctx->is_overmem) {
1549 		ctx->is_overmem = ISC_TRUE;
1550 	}
1551 
1552 	if (ctx->hi_water != 0U && !ctx->hi_called &&
1553 	    ctx->inuse > ctx->hi_water) {
1554 		ctx->hi_called = ISC_TRUE;
1555 		call_water = ISC_TRUE;
1556 	}
1557 	if (ctx->inuse > ctx->maxinuse) {
1558 		ctx->maxinuse = ctx->inuse;
1559 		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1560 		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1561 			fprintf(stderr, "maxinuse = %lu\n",
1562 				(unsigned long)ctx->inuse);
1563 	}
1564 	MCTXUNLOCK(ctx, &ctx->lock);
1565 
1566 	if (call_water)
1567 		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1568 
1569 	return (si);
1570 }
1571 
1572 ISC_MEMFUNC_SCOPE void *
isc___mem_reallocate(isc_mem_t * ctx0,void * ptr,size_t size FLARG)1573 isc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1574 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1575 	void *new_ptr = NULL;
1576 	size_t oldsize, copysize;
1577 
1578 	REQUIRE(VALID_CONTEXT(ctx));
1579 
1580 	/*
1581 	 * This function emulates the realloc(3) standard library function:
1582 	 * - if size > 0, allocate new memory; and if ptr is non NULL, copy
1583 	 *   as much of the old contents to the new buffer and free the old one.
1584 	 *   Note that when allocation fails the original pointer is intact;
1585 	 *   the caller must free it.
1586 	 * - if size is 0 and ptr is non NULL, simply free the given ptr.
1587 	 * - this function returns:
1588 	 *     pointer to the newly allocated memory, or
1589 	 *     NULL if allocation fails or doesn't happen.
1590 	 */
1591 	if (size > 0U) {
1592 		new_ptr = isc__mem_allocate(ctx0, size FLARG_PASS);
1593 		if (new_ptr != NULL && ptr != NULL) {
1594 			oldsize = (((size_info *)ptr)[-1]).u.size;
1595 			INSIST(oldsize >= ALIGNMENT_SIZE);
1596 			oldsize -= ALIGNMENT_SIZE;
1597 			copysize = oldsize > size ? size : oldsize;
1598 			memcpy(new_ptr, ptr, copysize);
1599 			isc__mem_free(ctx0, ptr FLARG_PASS);
1600 		}
1601 	} else if (ptr != NULL)
1602 		isc__mem_free(ctx0, ptr FLARG_PASS);
1603 
1604 	return (new_ptr);
1605 }
1606 
1607 ISC_MEMFUNC_SCOPE void
isc___mem_free(isc_mem_t * ctx0,void * ptr FLARG)1608 isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) {
1609 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1610 	size_info *si;
1611 	size_t size;
1612 	isc_boolean_t call_water= ISC_FALSE;
1613 
1614 	REQUIRE(VALID_CONTEXT(ctx));
1615 	REQUIRE(ptr != NULL);
1616 
1617 	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1618 		si = &(((size_info *)ptr)[-2]);
1619 		REQUIRE(si->u.ctx == ctx);
1620 		size = si[1].u.size;
1621 	} else {
1622 		si = &(((size_info *)ptr)[-1]);
1623 		size = si->u.size;
1624 	}
1625 
1626 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1627 		MCTXLOCK(ctx, &ctx->lock);
1628 		mem_putunlocked(ctx, si, size);
1629 	} else {
1630 		mem_put(ctx, si, size);
1631 		MCTXLOCK(ctx, &ctx->lock);
1632 		mem_putstats(ctx, si, size);
1633 	}
1634 
1635 	DELETE_TRACE(ctx, ptr, size, file, line);
1636 
1637 	/*
1638 	 * The check against ctx->lo_water == 0 is for the condition
1639 	 * when the context was pushed over hi_water but then had
1640 	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1641 	 */
1642 	if (ctx->is_overmem &&
1643 	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1644 		ctx->is_overmem = ISC_FALSE;
1645 	}
1646 
1647 	if (ctx->hi_called &&
1648 	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1649 		ctx->hi_called = ISC_FALSE;
1650 
1651 		if (ctx->water != NULL)
1652 			call_water = ISC_TRUE;
1653 	}
1654 	MCTXUNLOCK(ctx, &ctx->lock);
1655 
1656 	if (call_water)
1657 		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1658 }
1659 
1660 
1661 /*
1662  * Other useful things.
1663  */
1664 
1665 ISC_MEMFUNC_SCOPE char *
isc___mem_strdup(isc_mem_t * mctx0,const char * s FLARG)1666 isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) {
1667 	isc__mem_t *mctx = (isc__mem_t *)mctx0;
1668 	size_t len;
1669 	char *ns;
1670 
1671 	REQUIRE(VALID_CONTEXT(mctx));
1672 	REQUIRE(s != NULL);
1673 
1674 	len = strlen(s);
1675 
1676 	ns = isc___mem_allocate((isc_mem_t *)mctx, len + 1 FLARG_PASS);
1677 
1678 	if (ns != NULL)
1679 		strncpy(ns, s, len + 1);
1680 
1681 	return (ns);
1682 }
1683 
1684 ISC_MEMFUNC_SCOPE void
isc__mem_setdestroycheck(isc_mem_t * ctx0,isc_boolean_t flag)1685 isc__mem_setdestroycheck(isc_mem_t *ctx0, isc_boolean_t flag) {
1686 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1687 
1688 	REQUIRE(VALID_CONTEXT(ctx));
1689 	MCTXLOCK(ctx, &ctx->lock);
1690 
1691 	ctx->checkfree = flag;
1692 
1693 	MCTXUNLOCK(ctx, &ctx->lock);
1694 }
1695 
1696 /*
1697  * Quotas
1698  */
1699 
1700 ISC_MEMFUNC_SCOPE void
isc__mem_setquota(isc_mem_t * ctx0,size_t quota)1701 isc__mem_setquota(isc_mem_t *ctx0, size_t quota) {
1702 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1703 
1704 	REQUIRE(VALID_CONTEXT(ctx));
1705 	MCTXLOCK(ctx, &ctx->lock);
1706 
1707 	ctx->quota = quota;
1708 
1709 	MCTXUNLOCK(ctx, &ctx->lock);
1710 }
1711 
1712 ISC_MEMFUNC_SCOPE size_t
isc__mem_getquota(isc_mem_t * ctx0)1713 isc__mem_getquota(isc_mem_t *ctx0) {
1714 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1715 	size_t quota;
1716 
1717 	REQUIRE(VALID_CONTEXT(ctx));
1718 	MCTXLOCK(ctx, &ctx->lock);
1719 
1720 	quota = ctx->quota;
1721 
1722 	MCTXUNLOCK(ctx, &ctx->lock);
1723 
1724 	return (quota);
1725 }
1726 
1727 ISC_MEMFUNC_SCOPE size_t
isc__mem_inuse(isc_mem_t * ctx0)1728 isc__mem_inuse(isc_mem_t *ctx0) {
1729 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1730 	size_t inuse;
1731 
1732 	REQUIRE(VALID_CONTEXT(ctx));
1733 	MCTXLOCK(ctx, &ctx->lock);
1734 
1735 	inuse = ctx->inuse;
1736 
1737 	MCTXUNLOCK(ctx, &ctx->lock);
1738 
1739 	return (inuse);
1740 }
1741 
1742 ISC_MEMFUNC_SCOPE void
isc__mem_setwater(isc_mem_t * ctx0,isc_mem_water_t water,void * water_arg,size_t hiwater,size_t lowater)1743 isc__mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg,
1744 		 size_t hiwater, size_t lowater)
1745 {
1746 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1747 	isc_boolean_t callwater = ISC_FALSE;
1748 	isc_mem_water_t oldwater;
1749 	void *oldwater_arg;
1750 
1751 	REQUIRE(VALID_CONTEXT(ctx));
1752 	REQUIRE(hiwater >= lowater);
1753 
1754 	MCTXLOCK(ctx, &ctx->lock);
1755 	oldwater = ctx->water;
1756 	oldwater_arg = ctx->water_arg;
1757 	if (water == NULL) {
1758 		callwater = ctx->hi_called;
1759 		ctx->water = NULL;
1760 		ctx->water_arg = NULL;
1761 		ctx->hi_water = 0;
1762 		ctx->lo_water = 0;
1763 		ctx->hi_called = ISC_FALSE;
1764 	} else {
1765 		if (ctx->hi_called &&
1766 		    (ctx->water != water || ctx->water_arg != water_arg ||
1767 		     ctx->inuse < lowater || lowater == 0U))
1768 			callwater = ISC_TRUE;
1769 		ctx->water = water;
1770 		ctx->water_arg = water_arg;
1771 		ctx->hi_water = hiwater;
1772 		ctx->lo_water = lowater;
1773 		ctx->hi_called = ISC_FALSE;
1774 	}
1775 	MCTXUNLOCK(ctx, &ctx->lock);
1776 
1777 	if (callwater && oldwater != NULL)
1778 		(oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1779 }
1780 
1781 ISC_MEMFUNC_SCOPE isc_boolean_t
isc__mem_isovermem(isc_mem_t * ctx0)1782 isc__mem_isovermem(isc_mem_t *ctx0) {
1783 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1784 
1785 	REQUIRE(VALID_CONTEXT(ctx));
1786 
1787 	/*
1788 	 * We don't bother to lock the context because 100% accuracy isn't
1789 	 * necessary (and even if we locked the context the returned value
1790 	 * could be different from the actual state when it's used anyway)
1791 	 */
1792 	return (ctx->is_overmem);
1793 }
1794 
1795 ISC_MEMFUNC_SCOPE void
isc__mem_setname(isc_mem_t * ctx0,const char * name,void * tag)1796 isc__mem_setname(isc_mem_t *ctx0, const char *name, void *tag) {
1797 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1798 
1799 	REQUIRE(VALID_CONTEXT(ctx));
1800 
1801 	LOCK(&ctx->lock);
1802 	memset(ctx->name, 0, sizeof(ctx->name));
1803 	strncpy(ctx->name, name, sizeof(ctx->name) - 1);
1804 	ctx->tag = tag;
1805 	UNLOCK(&ctx->lock);
1806 }
1807 
1808 ISC_MEMFUNC_SCOPE const char *
isc__mem_getname(isc_mem_t * ctx0)1809 isc__mem_getname(isc_mem_t *ctx0) {
1810 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1811 
1812 	REQUIRE(VALID_CONTEXT(ctx));
1813 
1814 	return (ctx->name);
1815 }
1816 
1817 ISC_MEMFUNC_SCOPE void *
isc__mem_gettag(isc_mem_t * ctx0)1818 isc__mem_gettag(isc_mem_t *ctx0) {
1819 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1820 
1821 	REQUIRE(VALID_CONTEXT(ctx));
1822 
1823 	return (ctx->tag);
1824 }
1825 
1826 /*
1827  * Memory pool stuff
1828  */
1829 
1830 ISC_MEMFUNC_SCOPE isc_result_t
isc__mempool_create(isc_mem_t * mctx0,size_t size,isc_mempool_t ** mpctxp)1831 isc__mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) {
1832 	isc__mem_t *mctx = (isc__mem_t *)mctx0;
1833 	isc__mempool_t *mpctx;
1834 
1835 	REQUIRE(VALID_CONTEXT(mctx));
1836 	REQUIRE(size > 0U);
1837 	REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1838 
1839 	/*
1840 	 * Allocate space for this pool, initialize values, and if all works
1841 	 * well, attach to the memory context.
1842 	 */
1843 	mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t));
1844 	if (mpctx == NULL)
1845 		return (ISC_R_NOMEMORY);
1846 
1847 	mpctx->common.methods = (isc_mempoolmethods_t *)&mempoolmethods;
1848 	mpctx->common.impmagic = MEMPOOL_MAGIC;
1849 	mpctx->common.magic = ISCAPI_MPOOL_MAGIC;
1850 	mpctx->lock = NULL;
1851 	mpctx->mctx = mctx;
1852 	mpctx->size = size;
1853 	mpctx->maxalloc = UINT_MAX;
1854 	mpctx->allocated = 0;
1855 	mpctx->freecount = 0;
1856 	mpctx->freemax = 1;
1857 	mpctx->fillcount = 1;
1858 	mpctx->gets = 0;
1859 #if ISC_MEMPOOL_NAMES
1860 	mpctx->name[0] = 0;
1861 #endif
1862 	mpctx->items = NULL;
1863 
1864 	*mpctxp = (isc_mempool_t *)mpctx;
1865 
1866 	MCTXLOCK(mctx, &mctx->lock);
1867 	ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1868 	mctx->poolcnt++;
1869 	MCTXUNLOCK(mctx, &mctx->lock);
1870 
1871 	return (ISC_R_SUCCESS);
1872 }
1873 
1874 ISC_MEMFUNC_SCOPE void
isc__mempool_setname(isc_mempool_t * mpctx0,const char * name)1875 isc__mempool_setname(isc_mempool_t *mpctx0, const char *name) {
1876 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1877 
1878 	REQUIRE(name != NULL);
1879 	REQUIRE(VALID_MEMPOOL(mpctx));
1880 
1881 #if ISC_MEMPOOL_NAMES
1882 	if (mpctx->lock != NULL)
1883 		LOCK(mpctx->lock);
1884 
1885 	strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
1886 	mpctx->name[sizeof(mpctx->name) - 1] = '\0';
1887 
1888 	if (mpctx->lock != NULL)
1889 		UNLOCK(mpctx->lock);
1890 #else
1891 	UNUSED(mpctx);
1892 	UNUSED(name);
1893 #endif
1894 }
1895 
1896 ISC_MEMFUNC_SCOPE void
isc__mempool_destroy(isc_mempool_t ** mpctxp)1897 isc__mempool_destroy(isc_mempool_t **mpctxp) {
1898 	isc__mempool_t *mpctx;
1899 	isc__mem_t *mctx;
1900 	isc_mutex_t *lock;
1901 	element *item;
1902 
1903 	REQUIRE(mpctxp != NULL);
1904 	mpctx = (isc__mempool_t *)*mpctxp;
1905 	REQUIRE(VALID_MEMPOOL(mpctx));
1906 #if ISC_MEMPOOL_NAMES
1907 	if (mpctx->allocated > 0)
1908 		UNEXPECTED_ERROR(__FILE__, __LINE__,
1909 				 "isc__mempool_destroy(): mempool %s "
1910 				 "leaked memory",
1911 				 mpctx->name);
1912 #endif
1913 	REQUIRE(mpctx->allocated == 0);
1914 
1915 	mctx = mpctx->mctx;
1916 
1917 	lock = mpctx->lock;
1918 
1919 	if (lock != NULL)
1920 		LOCK(lock);
1921 
1922 	/*
1923 	 * Return any items on the free list
1924 	 */
1925 	MCTXLOCK(mctx, &mctx->lock);
1926 	while (mpctx->items != NULL) {
1927 		INSIST(mpctx->freecount > 0);
1928 		mpctx->freecount--;
1929 		item = mpctx->items;
1930 		mpctx->items = item->next;
1931 
1932 		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1933 			mem_putunlocked(mctx, item, mpctx->size);
1934 		} else {
1935 			mem_put(mctx, item, mpctx->size);
1936 			mem_putstats(mctx, item, mpctx->size);
1937 		}
1938 	}
1939 	MCTXUNLOCK(mctx, &mctx->lock);
1940 
1941 	/*
1942 	 * Remove our linked list entry from the memory context.
1943 	 */
1944 	MCTXLOCK(mctx, &mctx->lock);
1945 	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1946 	mctx->poolcnt--;
1947 	MCTXUNLOCK(mctx, &mctx->lock);
1948 
1949 	mpctx->common.impmagic = 0;
1950 	mpctx->common.magic = 0;
1951 
1952 	isc_mem_put((isc_mem_t *)mpctx->mctx, mpctx, sizeof(isc__mempool_t));
1953 
1954 	if (lock != NULL)
1955 		UNLOCK(lock);
1956 
1957 	*mpctxp = NULL;
1958 }
1959 
1960 ISC_MEMFUNC_SCOPE void
isc__mempool_associatelock(isc_mempool_t * mpctx0,isc_mutex_t * lock)1961 isc__mempool_associatelock(isc_mempool_t *mpctx0, isc_mutex_t *lock) {
1962 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1963 
1964 	REQUIRE(VALID_MEMPOOL(mpctx));
1965 	REQUIRE(mpctx->lock == NULL);
1966 	REQUIRE(lock != NULL);
1967 
1968 	mpctx->lock = lock;
1969 }
1970 
1971 ISC_MEMFUNC_SCOPE void *
isc___mempool_get(isc_mempool_t * mpctx0 FLARG)1972 isc___mempool_get(isc_mempool_t *mpctx0 FLARG) {
1973 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1974 	element *item;
1975 	isc__mem_t *mctx;
1976 	unsigned int i;
1977 
1978 	REQUIRE(VALID_MEMPOOL(mpctx));
1979 
1980 	mctx = mpctx->mctx;
1981 
1982 	if (mpctx->lock != NULL)
1983 		LOCK(mpctx->lock);
1984 
1985 	/*
1986 	 * Don't let the caller go over quota
1987 	 */
1988 	if (mpctx->allocated >= mpctx->maxalloc) {
1989 		item = NULL;
1990 		goto out;
1991 	}
1992 
1993 	/*
1994 	 * if we have a free list item, return the first here
1995 	 */
1996 	item = mpctx->items;
1997 	if (item != NULL) {
1998 		mpctx->items = item->next;
1999 		INSIST(mpctx->freecount > 0);
2000 		mpctx->freecount--;
2001 		mpctx->gets++;
2002 		mpctx->allocated++;
2003 		goto out;
2004 	}
2005 
2006 	/*
2007 	 * We need to dip into the well.  Lock the memory context here and
2008 	 * fill up our free list.
2009 	 */
2010 	MCTXLOCK(mctx, &mctx->lock);
2011 	for (i = 0; i < mpctx->fillcount; i++) {
2012 		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2013 			item = mem_getunlocked(mctx, mpctx->size);
2014 		} else {
2015 			item = mem_get(mctx, mpctx->size);
2016 			if (item != NULL)
2017 				mem_getstats(mctx, mpctx->size);
2018 		}
2019 		if (item == NULL)
2020 			break;
2021 		item->next = mpctx->items;
2022 		mpctx->items = item;
2023 		mpctx->freecount++;
2024 	}
2025 	MCTXUNLOCK(mctx, &mctx->lock);
2026 
2027 	/*
2028 	 * If we didn't get any items, return NULL.
2029 	 */
2030 	item = mpctx->items;
2031 	if (item == NULL)
2032 		goto out;
2033 
2034 	mpctx->items = item->next;
2035 	mpctx->freecount--;
2036 	mpctx->gets++;
2037 	mpctx->allocated++;
2038 
2039  out:
2040 	if (mpctx->lock != NULL)
2041 		UNLOCK(mpctx->lock);
2042 
2043 #if ISC_MEM_TRACKLINES
2044 	if (item != NULL) {
2045 		MCTXLOCK(mctx, &mctx->lock);
2046 		ADD_TRACE(mctx, item, mpctx->size, file, line);
2047 		MCTXUNLOCK(mctx, &mctx->lock);
2048 	}
2049 #endif /* ISC_MEM_TRACKLINES */
2050 
2051 	return (item);
2052 }
2053 
2054 ISC_MEMFUNC_SCOPE void
isc___mempool_put(isc_mempool_t * mpctx0,void * mem FLARG)2055 isc___mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
2056 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2057 	isc__mem_t *mctx;
2058 	element *item;
2059 
2060 	REQUIRE(VALID_MEMPOOL(mpctx));
2061 	REQUIRE(mem != NULL);
2062 
2063 	mctx = mpctx->mctx;
2064 
2065 	if (mpctx->lock != NULL)
2066 		LOCK(mpctx->lock);
2067 
2068 	INSIST(mpctx->allocated > 0);
2069 	mpctx->allocated--;
2070 
2071 #if ISC_MEM_TRACKLINES
2072 	MCTXLOCK(mctx, &mctx->lock);
2073 	DELETE_TRACE(mctx, mem, mpctx->size, file, line);
2074 	MCTXUNLOCK(mctx, &mctx->lock);
2075 #endif /* ISC_MEM_TRACKLINES */
2076 
2077 	/*
2078 	 * If our free list is full, return this to the mctx directly.
2079 	 */
2080 	if (mpctx->freecount >= mpctx->freemax) {
2081 		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2082 			MCTXLOCK(mctx, &mctx->lock);
2083 			mem_putunlocked(mctx, mem, mpctx->size);
2084 			MCTXUNLOCK(mctx, &mctx->lock);
2085 		} else {
2086 			mem_put(mctx, mem, mpctx->size);
2087 			MCTXLOCK(mctx, &mctx->lock);
2088 			mem_putstats(mctx, mem, mpctx->size);
2089 			MCTXUNLOCK(mctx, &mctx->lock);
2090 		}
2091 		if (mpctx->lock != NULL)
2092 			UNLOCK(mpctx->lock);
2093 		return;
2094 	}
2095 
2096 	/*
2097 	 * Otherwise, attach it to our free list and bump the counter.
2098 	 */
2099 	mpctx->freecount++;
2100 	item = (element *)mem;
2101 	item->next = mpctx->items;
2102 	mpctx->items = item;
2103 
2104 	if (mpctx->lock != NULL)
2105 		UNLOCK(mpctx->lock);
2106 }
2107 
2108 /*
2109  * Quotas
2110  */
2111 
2112 ISC_MEMFUNC_SCOPE void
isc__mempool_setfreemax(isc_mempool_t * mpctx0,unsigned int limit)2113 isc__mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) {
2114 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2115 
2116 	REQUIRE(VALID_MEMPOOL(mpctx));
2117 
2118 	if (mpctx->lock != NULL)
2119 		LOCK(mpctx->lock);
2120 
2121 	mpctx->freemax = limit;
2122 
2123 	if (mpctx->lock != NULL)
2124 		UNLOCK(mpctx->lock);
2125 }
2126 
2127 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getfreemax(isc_mempool_t * mpctx0)2128 isc__mempool_getfreemax(isc_mempool_t *mpctx0) {
2129 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2130 	unsigned int freemax;
2131 
2132 	REQUIRE(VALID_MEMPOOL(mpctx));
2133 
2134 	if (mpctx->lock != NULL)
2135 		LOCK(mpctx->lock);
2136 
2137 	freemax = mpctx->freemax;
2138 
2139 	if (mpctx->lock != NULL)
2140 		UNLOCK(mpctx->lock);
2141 
2142 	return (freemax);
2143 }
2144 
2145 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getfreecount(isc_mempool_t * mpctx0)2146 isc__mempool_getfreecount(isc_mempool_t *mpctx0) {
2147 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2148 	unsigned int freecount;
2149 
2150 	REQUIRE(VALID_MEMPOOL(mpctx));
2151 
2152 	if (mpctx->lock != NULL)
2153 		LOCK(mpctx->lock);
2154 
2155 	freecount = mpctx->freecount;
2156 
2157 	if (mpctx->lock != NULL)
2158 		UNLOCK(mpctx->lock);
2159 
2160 	return (freecount);
2161 }
2162 
2163 ISC_MEMFUNC_SCOPE void
isc__mempool_setmaxalloc(isc_mempool_t * mpctx0,unsigned int limit)2164 isc__mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) {
2165 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2166 
2167 	REQUIRE(limit > 0);
2168 
2169 	REQUIRE(VALID_MEMPOOL(mpctx));
2170 
2171 	if (mpctx->lock != NULL)
2172 		LOCK(mpctx->lock);
2173 
2174 	mpctx->maxalloc = limit;
2175 
2176 	if (mpctx->lock != NULL)
2177 		UNLOCK(mpctx->lock);
2178 }
2179 
2180 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getmaxalloc(isc_mempool_t * mpctx0)2181 isc__mempool_getmaxalloc(isc_mempool_t *mpctx0) {
2182 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2183 	unsigned int maxalloc;
2184 
2185 	REQUIRE(VALID_MEMPOOL(mpctx));
2186 
2187 	if (mpctx->lock != NULL)
2188 		LOCK(mpctx->lock);
2189 
2190 	maxalloc = mpctx->maxalloc;
2191 
2192 	if (mpctx->lock != NULL)
2193 		UNLOCK(mpctx->lock);
2194 
2195 	return (maxalloc);
2196 }
2197 
2198 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getallocated(isc_mempool_t * mpctx0)2199 isc__mempool_getallocated(isc_mempool_t *mpctx0) {
2200 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2201 	unsigned int allocated;
2202 
2203 	REQUIRE(VALID_MEMPOOL(mpctx));
2204 
2205 	if (mpctx->lock != NULL)
2206 		LOCK(mpctx->lock);
2207 
2208 	allocated = mpctx->allocated;
2209 
2210 	if (mpctx->lock != NULL)
2211 		UNLOCK(mpctx->lock);
2212 
2213 	return (allocated);
2214 }
2215 
2216 ISC_MEMFUNC_SCOPE void
isc__mempool_setfillcount(isc_mempool_t * mpctx0,unsigned int limit)2217 isc__mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) {
2218 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2219 
2220 	REQUIRE(limit > 0);
2221 	REQUIRE(VALID_MEMPOOL(mpctx));
2222 
2223 	if (mpctx->lock != NULL)
2224 		LOCK(mpctx->lock);
2225 
2226 	mpctx->fillcount = limit;
2227 
2228 	if (mpctx->lock != NULL)
2229 		UNLOCK(mpctx->lock);
2230 }
2231 
2232 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getfillcount(isc_mempool_t * mpctx0)2233 isc__mempool_getfillcount(isc_mempool_t *mpctx0) {
2234 	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2235 
2236 	unsigned int fillcount;
2237 
2238 	REQUIRE(VALID_MEMPOOL(mpctx));
2239 
2240 	if (mpctx->lock != NULL)
2241 		LOCK(mpctx->lock);
2242 
2243 	fillcount = mpctx->fillcount;
2244 
2245 	if (mpctx->lock != NULL)
2246 		UNLOCK(mpctx->lock);
2247 
2248 	return (fillcount);
2249 }
2250 
2251 #ifdef USE_MEMIMPREGISTER
2252 isc_result_t
isc__mem_register()2253 isc__mem_register() {
2254 	return (isc_mem_register(isc__mem_create2));
2255 }
2256 #endif
2257 
2258 #ifdef BIND9
2259 ISC_MEMFUNC_SCOPE void
isc__mem_printactive(isc_mem_t * ctx0,FILE * file)2260 isc__mem_printactive(isc_mem_t *ctx0, FILE *file) {
2261 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
2262 
2263 	REQUIRE(VALID_CONTEXT(ctx));
2264 	REQUIRE(file != NULL);
2265 
2266 #if !ISC_MEM_TRACKLINES
2267 	UNUSED(ctx);
2268 	UNUSED(file);
2269 #else
2270 	print_active(ctx, file);
2271 #endif
2272 }
2273 
2274 ISC_MEMFUNC_SCOPE void
isc__mem_printallactive(FILE * file)2275 isc__mem_printallactive(FILE *file) {
2276 #if !ISC_MEM_TRACKLINES
2277 	UNUSED(file);
2278 #else
2279 	isc__mem_t *ctx;
2280 
2281 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2282 
2283 	LOCK(&lock);
2284 	for (ctx = ISC_LIST_HEAD(contexts);
2285 	     ctx != NULL;
2286 	     ctx = ISC_LIST_NEXT(ctx, link)) {
2287 		fprintf(file, "context: %p\n", ctx);
2288 		print_active(ctx, file);
2289 	}
2290 	UNLOCK(&lock);
2291 #endif
2292 }
2293 
2294 ISC_MEMFUNC_SCOPE void
isc__mem_checkdestroyed(FILE * file)2295 isc__mem_checkdestroyed(FILE *file) {
2296 
2297 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2298 
2299 	LOCK(&lock);
2300 	if (!ISC_LIST_EMPTY(contexts))  {
2301 #if ISC_MEM_TRACKLINES
2302 		isc__mem_t *ctx;
2303 
2304 		for (ctx = ISC_LIST_HEAD(contexts);
2305 		     ctx != NULL;
2306 		     ctx = ISC_LIST_NEXT(ctx, link)) {
2307 			fprintf(file, "context: %p\n", ctx);
2308 			print_active(ctx, file);
2309 		}
2310 		fflush(file);
2311 #endif
2312 		INSIST(0);
2313 	}
2314 	UNLOCK(&lock);
2315 }
2316 
2317 ISC_MEMFUNC_SCOPE unsigned int
isc_mem_references(isc_mem_t * ctx0)2318 isc_mem_references(isc_mem_t *ctx0) {
2319 	isc__mem_t *ctx = (isc__mem_t *)ctx0;
2320 	unsigned int references;
2321 
2322 	REQUIRE(VALID_CONTEXT(ctx));
2323 
2324 	MCTXLOCK(ctx, &ctx->lock);
2325 	references = ctx->references;
2326 	MCTXUNLOCK(ctx, &ctx->lock);
2327 
2328 	return (references);
2329 }
2330 
2331 #ifdef HAVE_LIBXML2
2332 
2333 typedef struct summarystat {
2334 	isc_uint64_t	total;
2335 	isc_uint64_t	inuse;
2336 	isc_uint64_t	blocksize;
2337 	isc_uint64_t	contextsize;
2338 } summarystat_t;
2339 
2340 static void
renderctx(isc__mem_t * ctx,summarystat_t * summary,xmlTextWriterPtr writer)2341 renderctx(isc__mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
2342 	REQUIRE(VALID_CONTEXT(ctx));
2343 
2344 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "context");
2345 
2346 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
2347 	xmlTextWriterWriteFormatString(writer, "%p", ctx);
2348 	xmlTextWriterEndElement(writer); /* id */
2349 
2350 	if (ctx->name[0] != 0) {
2351 		xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
2352 		xmlTextWriterWriteFormatString(writer, "%s", ctx->name);
2353 		xmlTextWriterEndElement(writer); /* name */
2354 	}
2355 
2356 	REQUIRE(VALID_CONTEXT(ctx));
2357 	MCTXLOCK(ctx, &ctx->lock);
2358 
2359 	summary->contextsize += sizeof(*ctx) +
2360 		(ctx->max_size + 1) * sizeof(struct stats) +
2361 		ctx->max_size * sizeof(element *) +
2362 		ctx->basic_table_count * sizeof(char *);
2363 #if ISC_MEM_TRACKLINES
2364 	if (ctx->debuglist != NULL) {
2365 		summary->contextsize +=
2366 			(ctx->max_size + 1) * sizeof(debuglist_t) +
2367 			ctx->debuglistcnt * sizeof(debuglink_t);
2368 	}
2369 #endif
2370 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
2371 	xmlTextWriterWriteFormatString(writer, "%d", ctx->references);
2372 	xmlTextWriterEndElement(writer); /* references */
2373 
2374 	summary->total += ctx->total;
2375 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "total");
2376 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2377 				       (isc_uint64_t)ctx->total);
2378 	xmlTextWriterEndElement(writer); /* total */
2379 
2380 	summary->inuse += ctx->inuse;
2381 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse");
2382 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2383 				       (isc_uint64_t)ctx->inuse);
2384 	xmlTextWriterEndElement(writer); /* inuse */
2385 
2386 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse");
2387 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2388 				       (isc_uint64_t)ctx->maxinuse);
2389 	xmlTextWriterEndElement(writer); /* maxinuse */
2390 
2391 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize");
2392 	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2393 		summary->blocksize += ctx->basic_table_count *
2394 			NUM_BASIC_BLOCKS * ctx->mem_target;
2395 		xmlTextWriterWriteFormatString(writer,
2396 					       "%" ISC_PRINT_QUADFORMAT "u",
2397 					       (isc_uint64_t)
2398 					       ctx->basic_table_count *
2399 					       NUM_BASIC_BLOCKS *
2400 					       ctx->mem_target);
2401 	} else
2402 		xmlTextWriterWriteFormatString(writer, "%s", "-");
2403 	xmlTextWriterEndElement(writer); /* blocksize */
2404 
2405 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools");
2406 	xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt);
2407 	xmlTextWriterEndElement(writer); /* pools */
2408 	summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2409 
2410 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater");
2411 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2412 				       (isc_uint64_t)ctx->hi_water);
2413 	xmlTextWriterEndElement(writer); /* hiwater */
2414 
2415 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater");
2416 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2417 				       (isc_uint64_t)ctx->lo_water);
2418 	xmlTextWriterEndElement(writer); /* lowater */
2419 
2420 	MCTXUNLOCK(ctx, &ctx->lock);
2421 
2422 	xmlTextWriterEndElement(writer); /* context */
2423 }
2424 
2425 void
isc_mem_renderxml(xmlTextWriterPtr writer)2426 isc_mem_renderxml(xmlTextWriterPtr writer) {
2427 	isc__mem_t *ctx;
2428 	summarystat_t summary;
2429 	isc_uint64_t lost;
2430 
2431 	memset(&summary, 0, sizeof(summary));
2432 
2433 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts");
2434 
2435 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2436 
2437 	LOCK(&lock);
2438 	lost = totallost;
2439 	for (ctx = ISC_LIST_HEAD(contexts);
2440 	     ctx != NULL;
2441 	     ctx = ISC_LIST_NEXT(ctx, link)) {
2442 		renderctx(ctx, &summary, writer);
2443 	}
2444 	UNLOCK(&lock);
2445 
2446 	xmlTextWriterEndElement(writer); /* contexts */
2447 
2448 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary");
2449 
2450 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse");
2451 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2452 				       summary.total);
2453 	xmlTextWriterEndElement(writer); /* TotalUse */
2454 
2455 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse");
2456 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2457 				       summary.inuse);
2458 	xmlTextWriterEndElement(writer); /* InUse */
2459 
2460 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize");
2461 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2462 				       summary.blocksize);
2463 	xmlTextWriterEndElement(writer); /* BlockSize */
2464 
2465 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize");
2466 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2467 				       summary.contextsize);
2468 	xmlTextWriterEndElement(writer); /* ContextSize */
2469 
2470 	xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost");
2471 	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2472 				       lost);
2473 	xmlTextWriterEndElement(writer); /* Lost */
2474 
2475 	xmlTextWriterEndElement(writer); /* summary */
2476 }
2477 
2478 #endif /* HAVE_LIBXML2 */
2479 #endif /* BIND9 */
2480