mem.c revision 218384
1135446Strhodes/*
2214586Sdougb * Copyright (C) 2004-2010  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1997-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18218384Sdougb/* $Id: mem.c,v 1.145.120.9 2010-08-11 23:45:49 tbox Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24135446Strhodes#include <stdio.h>
25135446Strhodes#include <stdlib.h>
26135446Strhodes#include <stddef.h>
27135446Strhodes
28135446Strhodes#include <limits.h>
29135446Strhodes
30135446Strhodes#include <isc/magic.h>
31135446Strhodes#include <isc/mem.h>
32135446Strhodes#include <isc/msgs.h>
33170222Sdougb#include <isc/once.h>
34135446Strhodes#include <isc/ondestroy.h>
35135446Strhodes#include <isc/string.h>
36135446Strhodes#include <isc/mutex.h>
37193149Sdougb#include <isc/print.h>
38135446Strhodes#include <isc/util.h>
39193149Sdougb#include <isc/xml.h>
40135446Strhodes
41170222Sdougb#define MCTXLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) LOCK(l)
42170222Sdougb#define MCTXUNLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) UNLOCK(l)
43170222Sdougb
44135446Strhodes#ifndef ISC_MEM_DEBUGGING
45135446Strhodes#define ISC_MEM_DEBUGGING 0
46135446Strhodes#endif
47135446StrhodesLIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
48135446Strhodes
49135446Strhodes/*
50135446Strhodes * Constants.
51135446Strhodes */
52135446Strhodes
53135446Strhodes#define DEF_MAX_SIZE		1100
54135446Strhodes#define DEF_MEM_TARGET		4096
55193149Sdougb#define ALIGNMENT_SIZE		8U		/*%< must be a power of 2 */
56170222Sdougb#define NUM_BASIC_BLOCKS	64		/*%< must be > 1 */
57135446Strhodes#define TABLE_INCREMENT		1024
58135446Strhodes#define DEBUGLIST_COUNT		1024
59135446Strhodes
60135446Strhodes/*
61135446Strhodes * Types.
62135446Strhodes */
63135446Strhodes#if ISC_MEM_TRACKLINES
64135446Strhodestypedef struct debuglink debuglink_t;
65135446Strhodesstruct debuglink {
66135446Strhodes	ISC_LINK(debuglink_t)	link;
67135446Strhodes	const void	       *ptr[DEBUGLIST_COUNT];
68135446Strhodes	unsigned int		size[DEBUGLIST_COUNT];
69135446Strhodes	const char	       *file[DEBUGLIST_COUNT];
70135446Strhodes	unsigned int		line[DEBUGLIST_COUNT];
71135446Strhodes	unsigned int		count;
72135446Strhodes};
73135446Strhodes
74135446Strhodes#define FLARG_PASS	, file, line
75218384Sdougb#define FLARG		, const char *file, unsigned int line
76135446Strhodes#else
77135446Strhodes#define FLARG_PASS
78135446Strhodes#define FLARG
79135446Strhodes#endif
80135446Strhodes
81135446Strhodestypedef struct element element;
82135446Strhodesstruct element {
83135446Strhodes	element *		next;
84135446Strhodes};
85135446Strhodes
86135446Strhodestypedef struct {
87170222Sdougb	/*!
88135446Strhodes	 * This structure must be ALIGNMENT_SIZE bytes.
89135446Strhodes	 */
90135446Strhodes	union {
91135446Strhodes		size_t		size;
92170222Sdougb		isc_mem_t	*ctx;
93135446Strhodes		char		bytes[ALIGNMENT_SIZE];
94135446Strhodes	} u;
95135446Strhodes} size_info;
96135446Strhodes
97135446Strhodesstruct stats {
98135446Strhodes	unsigned long		gets;
99135446Strhodes	unsigned long		totalgets;
100135446Strhodes	unsigned long		blocks;
101135446Strhodes	unsigned long		freefrags;
102135446Strhodes};
103135446Strhodes
104135446Strhodes#define MEM_MAGIC		ISC_MAGIC('M', 'e', 'm', 'C')
105135446Strhodes#define VALID_CONTEXT(c)	ISC_MAGIC_VALID(c, MEM_MAGIC)
106135446Strhodes
107135446Strhodes#if ISC_MEM_TRACKLINES
108135446Strhodestypedef ISC_LIST(debuglink_t)	debuglist_t;
109135446Strhodes#endif
110135446Strhodes
111170222Sdougb/* List of all active memory contexts. */
112170222Sdougb
113170222Sdougbstatic ISC_LIST(isc_mem_t)	contexts;
114170222Sdougbstatic isc_once_t		once = ISC_ONCE_INIT;
115170222Sdougbstatic isc_mutex_t		lock;
116170222Sdougb
117193149Sdougb/*%
118193149Sdougb * Total size of lost memory due to a bug of external library.
119193149Sdougb * Locked by the global lock.
120193149Sdougb */
121193149Sdougbstatic isc_uint64_t		totallost;
122193149Sdougb
123135446Strhodesstruct isc_mem {
124135446Strhodes	unsigned int		magic;
125135446Strhodes	isc_ondestroy_t		ondestroy;
126170222Sdougb	unsigned int		flags;
127135446Strhodes	isc_mutex_t		lock;
128135446Strhodes	isc_memalloc_t		memalloc;
129135446Strhodes	isc_memfree_t		memfree;
130135446Strhodes	void *			arg;
131135446Strhodes	size_t			max_size;
132135446Strhodes	isc_boolean_t		checkfree;
133135446Strhodes	struct stats *		stats;
134135446Strhodes	unsigned int		references;
135193149Sdougb	char			name[16];
136193149Sdougb	void *			tag;
137135446Strhodes	size_t			quota;
138135446Strhodes	size_t			total;
139135446Strhodes	size_t			inuse;
140135446Strhodes	size_t			maxinuse;
141135446Strhodes	size_t			hi_water;
142135446Strhodes	size_t			lo_water;
143135446Strhodes	isc_boolean_t		hi_called;
144214586Sdougb	isc_boolean_t		is_overmem;
145135446Strhodes	isc_mem_water_t		water;
146135446Strhodes	void *			water_arg;
147135446Strhodes	ISC_LIST(isc_mempool_t)	pools;
148193149Sdougb	unsigned int		poolcnt;
149135446Strhodes
150170222Sdougb	/*  ISC_MEMFLAG_INTERNAL */
151135446Strhodes	size_t			mem_target;
152135446Strhodes	element **		freelists;
153135446Strhodes	element *		basic_blocks;
154135446Strhodes	unsigned char **	basic_table;
155135446Strhodes	unsigned int		basic_table_count;
156135446Strhodes	unsigned int		basic_table_size;
157135446Strhodes	unsigned char *		lowest;
158135446Strhodes	unsigned char *		highest;
159135446Strhodes
160135446Strhodes#if ISC_MEM_TRACKLINES
161135446Strhodes	debuglist_t *	 	debuglist;
162193149Sdougb	unsigned int		debuglistcnt;
163135446Strhodes#endif
164135446Strhodes
165135446Strhodes	unsigned int		memalloc_failures;
166170222Sdougb	ISC_LINK(isc_mem_t)	link;
167135446Strhodes};
168135446Strhodes
169135446Strhodes#define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
170135446Strhodes#define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
171135446Strhodes
172135446Strhodesstruct isc_mempool {
173135446Strhodes	/* always unlocked */
174170222Sdougb	unsigned int	magic;		/*%< magic number */
175170222Sdougb	isc_mutex_t    *lock;		/*%< optional lock */
176170222Sdougb	isc_mem_t      *mctx;		/*%< our memory context */
177170222Sdougb	/*%< locked via the memory context's lock */
178170222Sdougb	ISC_LINK(isc_mempool_t)	link;	/*%< next pool in this mem context */
179170222Sdougb	/*%< optionally locked from here down */
180170222Sdougb	element	       *items;		/*%< low water item list */
181170222Sdougb	size_t		size;		/*%< size of each item on this pool */
182170222Sdougb	unsigned int	maxalloc;	/*%< max number of items allowed */
183170222Sdougb	unsigned int	allocated;	/*%< # of items currently given out */
184170222Sdougb	unsigned int	freecount;	/*%< # of items on reserved list */
185170222Sdougb	unsigned int	freemax;	/*%< # of items allowed on free list */
186170222Sdougb	unsigned int	fillcount;	/*%< # of items to fetch on each fill */
187170222Sdougb	/*%< Stats only. */
188170222Sdougb	unsigned int	gets;		/*%< # of requests to this pool */
189170222Sdougb	/*%< Debugging only. */
190135446Strhodes#if ISC_MEMPOOL_NAMES
191170222Sdougb	char		name[16];	/*%< printed name in stats reports */
192135446Strhodes#endif
193135446Strhodes};
194135446Strhodes
195135446Strhodes/*
196135446Strhodes * Private Inline-able.
197135446Strhodes */
198135446Strhodes
199135446Strhodes#if ! ISC_MEM_TRACKLINES
200135446Strhodes#define ADD_TRACE(a, b, c, d, e)
201135446Strhodes#define DELETE_TRACE(a, b, c, d, e)
202135446Strhodes#else
203135446Strhodes#define ADD_TRACE(a, b, c, d, e) \
204135446Strhodes	do { \
205135446Strhodes		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
206135446Strhodes					  ISC_MEM_DEBUGRECORD)) != 0 && \
207135446Strhodes		     b != NULL) \
208186462Sdougb			 add_trace_entry(a, b, c, d, e); \
209135446Strhodes	} while (0)
210135446Strhodes#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)
211135446Strhodes
212135446Strhodesstatic void
213135446Strhodesprint_active(isc_mem_t *ctx, FILE *out);
214135446Strhodes
215170222Sdougb/*!
216135446Strhodes * mctx must be locked.
217135446Strhodes */
218135446Strhodesstatic inline void
219135446Strhodesadd_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size
220135446Strhodes		FLARG)
221135446Strhodes{
222135446Strhodes	debuglink_t *dl;
223135446Strhodes	unsigned int i;
224218384Sdougb	unsigned int mysize = size;
225135446Strhodes
226135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
227135446Strhodes		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
228135446Strhodes					       ISC_MSG_ADDTRACE,
229135446Strhodes					       "add %p size %u "
230135446Strhodes					       "file %s line %u mctx %p\n"),
231135446Strhodes			ptr, size, file, line, mctx);
232135446Strhodes
233135446Strhodes	if (mctx->debuglist == NULL)
234135446Strhodes		return;
235135446Strhodes
236218384Sdougb	if (mysize > mctx->max_size)
237218384Sdougb		mysize = mctx->max_size;
238135446Strhodes
239218384Sdougb	dl = ISC_LIST_HEAD(mctx->debuglist[mysize]);
240135446Strhodes	while (dl != NULL) {
241135446Strhodes		if (dl->count == DEBUGLIST_COUNT)
242135446Strhodes			goto next;
243135446Strhodes		for (i = 0; i < DEBUGLIST_COUNT; i++) {
244135446Strhodes			if (dl->ptr[i] == NULL) {
245135446Strhodes				dl->ptr[i] = ptr;
246135446Strhodes				dl->size[i] = size;
247135446Strhodes				dl->file[i] = file;
248135446Strhodes				dl->line[i] = line;
249135446Strhodes				dl->count++;
250135446Strhodes				return;
251135446Strhodes			}
252135446Strhodes		}
253135446Strhodes	next:
254135446Strhodes		dl = ISC_LIST_NEXT(dl, link);
255135446Strhodes	}
256135446Strhodes
257135446Strhodes	dl = malloc(sizeof(debuglink_t));
258135446Strhodes	INSIST(dl != NULL);
259135446Strhodes
260135446Strhodes	ISC_LINK_INIT(dl, link);
261135446Strhodes	for (i = 1; i < DEBUGLIST_COUNT; i++) {
262135446Strhodes		dl->ptr[i] = NULL;
263135446Strhodes		dl->size[i] = 0;
264135446Strhodes		dl->file[i] = NULL;
265135446Strhodes		dl->line[i] = 0;
266135446Strhodes	}
267135446Strhodes
268135446Strhodes	dl->ptr[0] = ptr;
269135446Strhodes	dl->size[0] = size;
270135446Strhodes	dl->file[0] = file;
271135446Strhodes	dl->line[0] = line;
272135446Strhodes	dl->count = 1;
273135446Strhodes
274218384Sdougb	ISC_LIST_PREPEND(mctx->debuglist[mysize], dl, link);
275193149Sdougb	mctx->debuglistcnt++;
276135446Strhodes}
277135446Strhodes
278135446Strhodesstatic inline void
279135446Strhodesdelete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size,
280135446Strhodes		   const char *file, unsigned int line)
281135446Strhodes{
282135446Strhodes	debuglink_t *dl;
283135446Strhodes	unsigned int i;
284135446Strhodes
285135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
286135446Strhodes		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
287135446Strhodes					       ISC_MSG_DELTRACE,
288135446Strhodes					       "del %p size %u "
289135446Strhodes					       "file %s line %u mctx %p\n"),
290135446Strhodes			ptr, size, file, line, mctx);
291135446Strhodes
292135446Strhodes	if (mctx->debuglist == NULL)
293135446Strhodes		return;
294135446Strhodes
295135446Strhodes	if (size > mctx->max_size)
296135446Strhodes		size = mctx->max_size;
297135446Strhodes
298135446Strhodes	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
299135446Strhodes	while (dl != NULL) {
300135446Strhodes		for (i = 0; i < DEBUGLIST_COUNT; i++) {
301135446Strhodes			if (dl->ptr[i] == ptr) {
302135446Strhodes				dl->ptr[i] = NULL;
303135446Strhodes				dl->size[i] = 0;
304135446Strhodes				dl->file[i] = NULL;
305135446Strhodes				dl->line[i] = 0;
306135446Strhodes
307135446Strhodes				INSIST(dl->count > 0);
308135446Strhodes				dl->count--;
309135446Strhodes				if (dl->count == 0) {
310135446Strhodes					ISC_LIST_UNLINK(mctx->debuglist[size],
311135446Strhodes							dl, link);
312135446Strhodes					free(dl);
313135446Strhodes				}
314135446Strhodes				return;
315135446Strhodes			}
316135446Strhodes		}
317135446Strhodes		dl = ISC_LIST_NEXT(dl, link);
318135446Strhodes	}
319135446Strhodes
320135446Strhodes	/*
321135446Strhodes	 * If we get here, we didn't find the item on the list.  We're
322135446Strhodes	 * screwed.
323135446Strhodes	 */
324135446Strhodes	INSIST(dl != NULL);
325135446Strhodes}
326135446Strhodes#endif /* ISC_MEM_TRACKLINES */
327135446Strhodes
328135446Strhodesstatic inline size_t
329135446Strhodesrmsize(size_t size) {
330135446Strhodes	/*
331186462Sdougb	 * round down to ALIGNMENT_SIZE
332135446Strhodes	 */
333135446Strhodes	return (size & (~(ALIGNMENT_SIZE - 1)));
334135446Strhodes}
335135446Strhodes
336135446Strhodesstatic inline size_t
337135446Strhodesquantize(size_t size) {
338170222Sdougb	/*!
339135446Strhodes	 * Round up the result in order to get a size big
340135446Strhodes	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
341135446Strhodes	 * byte boundaries.
342135446Strhodes	 */
343135446Strhodes
344170222Sdougb	if (size == 0U)
345135446Strhodes		return (ALIGNMENT_SIZE);
346135446Strhodes	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
347135446Strhodes}
348135446Strhodes
349135446Strhodesstatic inline isc_boolean_t
350135446Strhodesmore_basic_blocks(isc_mem_t *ctx) {
351135446Strhodes	void *new;
352135446Strhodes	unsigned char *curr, *next;
353135446Strhodes	unsigned char *first, *last;
354135446Strhodes	unsigned char **table;
355135446Strhodes	unsigned int table_size;
356135446Strhodes	size_t increment;
357135446Strhodes	int i;
358135446Strhodes
359135446Strhodes	/* Require: we hold the context lock. */
360135446Strhodes
361135446Strhodes	/*
362135446Strhodes	 * Did we hit the quota for this context?
363135446Strhodes	 */
364135446Strhodes	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
365170222Sdougb	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
366135446Strhodes		return (ISC_FALSE);
367135446Strhodes
368135446Strhodes	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
369135446Strhodes	if (ctx->basic_table_count == ctx->basic_table_size) {
370135446Strhodes		table_size = ctx->basic_table_size + TABLE_INCREMENT;
371135446Strhodes		table = (ctx->memalloc)(ctx->arg,
372135446Strhodes					table_size * sizeof(unsigned char *));
373135446Strhodes		if (table == NULL) {
374135446Strhodes			ctx->memalloc_failures++;
375135446Strhodes			return (ISC_FALSE);
376135446Strhodes		}
377135446Strhodes		if (ctx->basic_table_size != 0) {
378135446Strhodes			memcpy(table, ctx->basic_table,
379135446Strhodes			       ctx->basic_table_size *
380135446Strhodes			       sizeof(unsigned char *));
381135446Strhodes			(ctx->memfree)(ctx->arg, ctx->basic_table);
382135446Strhodes		}
383135446Strhodes		ctx->basic_table = table;
384135446Strhodes		ctx->basic_table_size = table_size;
385135446Strhodes	}
386135446Strhodes
387135446Strhodes	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
388135446Strhodes	if (new == NULL) {
389135446Strhodes		ctx->memalloc_failures++;
390135446Strhodes		return (ISC_FALSE);
391135446Strhodes	}
392135446Strhodes	ctx->total += increment;
393135446Strhodes	ctx->basic_table[ctx->basic_table_count] = new;
394135446Strhodes	ctx->basic_table_count++;
395135446Strhodes
396135446Strhodes	curr = new;
397135446Strhodes	next = curr + ctx->mem_target;
398135446Strhodes	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
399135446Strhodes		((element *)curr)->next = (element *)next;
400135446Strhodes		curr = next;
401135446Strhodes		next += ctx->mem_target;
402135446Strhodes	}
403135446Strhodes	/*
404135446Strhodes	 * curr is now pointing at the last block in the
405135446Strhodes	 * array.
406135446Strhodes	 */
407135446Strhodes	((element *)curr)->next = NULL;
408135446Strhodes	first = new;
409135446Strhodes	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
410135446Strhodes	if (first < ctx->lowest || ctx->lowest == NULL)
411135446Strhodes		ctx->lowest = first;
412135446Strhodes	if (last > ctx->highest)
413135446Strhodes		ctx->highest = last;
414135446Strhodes	ctx->basic_blocks = new;
415135446Strhodes
416135446Strhodes	return (ISC_TRUE);
417135446Strhodes}
418135446Strhodes
419135446Strhodesstatic inline isc_boolean_t
420135446Strhodesmore_frags(isc_mem_t *ctx, size_t new_size) {
421135446Strhodes	int i, frags;
422135446Strhodes	size_t total_size;
423135446Strhodes	void *new;
424135446Strhodes	unsigned char *curr, *next;
425135446Strhodes
426170222Sdougb	/*!
427135446Strhodes	 * Try to get more fragments by chopping up a basic block.
428135446Strhodes	 */
429135446Strhodes
430135446Strhodes	if (ctx->basic_blocks == NULL) {
431135446Strhodes		if (!more_basic_blocks(ctx)) {
432135446Strhodes			/*
433135446Strhodes			 * We can't get more memory from the OS, or we've
434135446Strhodes			 * hit the quota for this context.
435135446Strhodes			 */
436135446Strhodes			/*
437135446Strhodes			 * XXXRTH  "At quota" notification here.
438135446Strhodes			 */
439135446Strhodes			return (ISC_FALSE);
440135446Strhodes		}
441135446Strhodes	}
442135446Strhodes
443135446Strhodes	total_size = ctx->mem_target;
444135446Strhodes	new = ctx->basic_blocks;
445135446Strhodes	ctx->basic_blocks = ctx->basic_blocks->next;
446135446Strhodes	frags = total_size / new_size;
447135446Strhodes	ctx->stats[new_size].blocks++;
448135446Strhodes	ctx->stats[new_size].freefrags += frags;
449135446Strhodes	/*
450135446Strhodes	 * Set up a linked-list of blocks of size
451135446Strhodes	 * "new_size".
452135446Strhodes	 */
453135446Strhodes	curr = new;
454135446Strhodes	next = curr + new_size;
455135446Strhodes	total_size -= new_size;
456135446Strhodes	for (i = 0; i < (frags - 1); i++) {
457135446Strhodes		((element *)curr)->next = (element *)next;
458135446Strhodes		curr = next;
459135446Strhodes		next += new_size;
460135446Strhodes		total_size -= new_size;
461135446Strhodes	}
462135446Strhodes	/*
463135446Strhodes	 * Add the remaining fragment of the basic block to a free list.
464135446Strhodes	 */
465135446Strhodes	total_size = rmsize(total_size);
466170222Sdougb	if (total_size > 0U) {
467135446Strhodes		((element *)next)->next = ctx->freelists[total_size];
468135446Strhodes		ctx->freelists[total_size] = (element *)next;
469135446Strhodes		ctx->stats[total_size].freefrags++;
470135446Strhodes	}
471135446Strhodes	/*
472135446Strhodes	 * curr is now pointing at the last block in the
473135446Strhodes	 * array.
474135446Strhodes	 */
475135446Strhodes	((element *)curr)->next = NULL;
476135446Strhodes	ctx->freelists[new_size] = new;
477135446Strhodes
478135446Strhodes	return (ISC_TRUE);
479135446Strhodes}
480135446Strhodes
481135446Strhodesstatic inline void *
482135446Strhodesmem_getunlocked(isc_mem_t *ctx, size_t size) {
483135446Strhodes	size_t new_size = quantize(size);
484135446Strhodes	void *ret;
485135446Strhodes
486135446Strhodes	if (size >= ctx->max_size || new_size >= ctx->max_size) {
487135446Strhodes		/*
488135446Strhodes		 * memget() was called on something beyond our upper limit.
489135446Strhodes		 */
490170222Sdougb		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
491135446Strhodes			ret = NULL;
492135446Strhodes			goto done;
493135446Strhodes		}
494135446Strhodes		ret = (ctx->memalloc)(ctx->arg, size);
495135446Strhodes		if (ret == NULL) {
496135446Strhodes			ctx->memalloc_failures++;
497135446Strhodes			goto done;
498135446Strhodes		}
499135446Strhodes		ctx->total += size;
500135446Strhodes		ctx->inuse += size;
501135446Strhodes		ctx->stats[ctx->max_size].gets++;
502135446Strhodes		ctx->stats[ctx->max_size].totalgets++;
503135446Strhodes		/*
504135446Strhodes		 * If we don't set new_size to size, then the
505135446Strhodes		 * ISC_MEM_FILL code might write over bytes we
506135446Strhodes		 * don't own.
507135446Strhodes		 */
508135446Strhodes		new_size = size;
509135446Strhodes		goto done;
510135446Strhodes	}
511135446Strhodes
512135446Strhodes	/*
513135446Strhodes	 * If there are no blocks in the free list for this size, get a chunk
514135446Strhodes	 * of memory and then break it up into "new_size"-sized blocks, adding
515135446Strhodes	 * them to the free list.
516135446Strhodes	 */
517135446Strhodes	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
518135446Strhodes		return (NULL);
519135446Strhodes
520135446Strhodes	/*
521135446Strhodes	 * The free list uses the "rounded-up" size "new_size".
522135446Strhodes	 */
523135446Strhodes	ret = ctx->freelists[new_size];
524135446Strhodes	ctx->freelists[new_size] = ctx->freelists[new_size]->next;
525135446Strhodes
526135446Strhodes	/*
527135446Strhodes	 * The stats[] uses the _actual_ "size" requested by the
528135446Strhodes	 * caller, with the caveat (in the code above) that "size" >= the
529135446Strhodes	 * max. size (max_size) ends up getting recorded as a call to
530135446Strhodes	 * max_size.
531135446Strhodes	 */
532135446Strhodes	ctx->stats[size].gets++;
533135446Strhodes	ctx->stats[size].totalgets++;
534135446Strhodes	ctx->stats[new_size].freefrags--;
535135446Strhodes	ctx->inuse += new_size;
536135446Strhodes
537135446Strhodes done:
538135446Strhodes
539135446Strhodes#if ISC_MEM_FILL
540135446Strhodes	if (ret != NULL)
541135446Strhodes		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
542135446Strhodes#endif
543135446Strhodes
544135446Strhodes	return (ret);
545135446Strhodes}
546135446Strhodes
547135446Strhodes#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
548135446Strhodesstatic inline void
549135446Strhodescheck_overrun(void *mem, size_t size, size_t new_size) {
550135446Strhodes	unsigned char *cp;
551135446Strhodes
552135446Strhodes	cp = (unsigned char *)mem;
553135446Strhodes	cp += size;
554135446Strhodes	while (size < new_size) {
555135446Strhodes		INSIST(*cp == 0xbe);
556135446Strhodes		cp++;
557135446Strhodes		size++;
558135446Strhodes	}
559135446Strhodes}
560135446Strhodes#endif
561135446Strhodes
562135446Strhodesstatic inline void
563135446Strhodesmem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
564135446Strhodes	size_t new_size = quantize(size);
565135446Strhodes
566135446Strhodes	if (size == ctx->max_size || new_size >= ctx->max_size) {
567135446Strhodes		/*
568135446Strhodes		 * memput() called on something beyond our upper limit.
569135446Strhodes		 */
570135446Strhodes#if ISC_MEM_FILL
571135446Strhodes		memset(mem, 0xde, size); /* Mnemonic for "dead". */
572135446Strhodes#endif
573135446Strhodes		(ctx->memfree)(ctx->arg, mem);
574170222Sdougb		INSIST(ctx->stats[ctx->max_size].gets != 0U);
575135446Strhodes		ctx->stats[ctx->max_size].gets--;
576135446Strhodes		INSIST(size <= ctx->total);
577135446Strhodes		ctx->inuse -= size;
578135446Strhodes		ctx->total -= size;
579135446Strhodes		return;
580135446Strhodes	}
581135446Strhodes
582135446Strhodes#if ISC_MEM_FILL
583135446Strhodes#if ISC_MEM_CHECKOVERRUN
584135446Strhodes	check_overrun(mem, size, new_size);
585135446Strhodes#endif
586135446Strhodes	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
587135446Strhodes#endif
588135446Strhodes
589135446Strhodes	/*
590135446Strhodes	 * The free list uses the "rounded-up" size "new_size".
591135446Strhodes	 */
592135446Strhodes	((element *)mem)->next = ctx->freelists[new_size];
593135446Strhodes	ctx->freelists[new_size] = (element *)mem;
594135446Strhodes
595135446Strhodes	/*
596135446Strhodes	 * The stats[] uses the _actual_ "size" requested by the
597135446Strhodes	 * caller, with the caveat (in the code above) that "size" >= the
598135446Strhodes	 * max. size (max_size) ends up getting recorded as a call to
599135446Strhodes	 * max_size.
600135446Strhodes	 */
601170222Sdougb	INSIST(ctx->stats[size].gets != 0U);
602135446Strhodes	ctx->stats[size].gets--;
603135446Strhodes	ctx->stats[new_size].freefrags++;
604135446Strhodes	ctx->inuse -= new_size;
605135446Strhodes}
606135446Strhodes
607170222Sdougb/*!
608135446Strhodes * Perform a malloc, doing memory filling and overrun detection as necessary.
609135446Strhodes */
610135446Strhodesstatic inline void *
611135446Strhodesmem_get(isc_mem_t *ctx, size_t size) {
612135446Strhodes	char *ret;
613135446Strhodes
614135446Strhodes#if ISC_MEM_CHECKOVERRUN
615135446Strhodes	size += 1;
616135446Strhodes#endif
617135446Strhodes
618135446Strhodes	ret = (ctx->memalloc)(ctx->arg, size);
619135446Strhodes	if (ret == NULL)
620186462Sdougb		ctx->memalloc_failures++;
621135446Strhodes
622135446Strhodes#if ISC_MEM_FILL
623135446Strhodes	if (ret != NULL)
624135446Strhodes		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
625135446Strhodes#else
626135446Strhodes#  if ISC_MEM_CHECKOVERRUN
627135446Strhodes	if (ret != NULL)
628135446Strhodes		ret[size-1] = 0xbe;
629135446Strhodes#  endif
630135446Strhodes#endif
631135446Strhodes
632135446Strhodes	return (ret);
633135446Strhodes}
634135446Strhodes
635170222Sdougb/*!
636135446Strhodes * Perform a free, doing memory filling and overrun detection as necessary.
637135446Strhodes */
638135446Strhodesstatic inline void
639135446Strhodesmem_put(isc_mem_t *ctx, void *mem, size_t size) {
640135446Strhodes#if ISC_MEM_CHECKOVERRUN
641135446Strhodes	INSIST(((unsigned char *)mem)[size] == 0xbe);
642135446Strhodes#endif
643135446Strhodes#if ISC_MEM_FILL
644135446Strhodes	memset(mem, 0xde, size); /* Mnemonic for "dead". */
645135446Strhodes#else
646135446Strhodes	UNUSED(size);
647135446Strhodes#endif
648135446Strhodes	(ctx->memfree)(ctx->arg, mem);
649135446Strhodes}
650135446Strhodes
651170222Sdougb/*!
652135446Strhodes * Update internal counters after a memory get.
653135446Strhodes */
654135446Strhodesstatic inline void
655135446Strhodesmem_getstats(isc_mem_t *ctx, size_t size) {
656135446Strhodes	ctx->total += size;
657135446Strhodes	ctx->inuse += size;
658135446Strhodes
659135446Strhodes	if (size > ctx->max_size) {
660135446Strhodes		ctx->stats[ctx->max_size].gets++;
661135446Strhodes		ctx->stats[ctx->max_size].totalgets++;
662135446Strhodes	} else {
663135446Strhodes		ctx->stats[size].gets++;
664135446Strhodes		ctx->stats[size].totalgets++;
665135446Strhodes	}
666135446Strhodes}
667135446Strhodes
668170222Sdougb/*!
669135446Strhodes * Update internal counters after a memory put.
670135446Strhodes */
671135446Strhodesstatic inline void
672135446Strhodesmem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
673135446Strhodes	UNUSED(ptr);
674135446Strhodes
675135446Strhodes	INSIST(ctx->inuse >= size);
676135446Strhodes	ctx->inuse -= size;
677135446Strhodes
678135446Strhodes	if (size > ctx->max_size) {
679135446Strhodes		INSIST(ctx->stats[ctx->max_size].gets > 0U);
680135446Strhodes		ctx->stats[ctx->max_size].gets--;
681135446Strhodes	} else {
682135446Strhodes		INSIST(ctx->stats[size].gets > 0U);
683135446Strhodes		ctx->stats[size].gets--;
684135446Strhodes	}
685135446Strhodes}
686135446Strhodes
687135446Strhodes/*
688135446Strhodes * Private.
689135446Strhodes */
690135446Strhodes
691135446Strhodesstatic void *
692135446Strhodesdefault_memalloc(void *arg, size_t size) {
693135446Strhodes	UNUSED(arg);
694135446Strhodes	if (size == 0U)
695135446Strhodes		size = 1;
696135446Strhodes	return (malloc(size));
697135446Strhodes}
698135446Strhodes
699135446Strhodesstatic void
700135446Strhodesdefault_memfree(void *arg, void *ptr) {
701135446Strhodes	UNUSED(arg);
702135446Strhodes	free(ptr);
703135446Strhodes}
704135446Strhodes
705170222Sdougbstatic void
706170222Sdougbinitialize_action(void) {
707174187Sdougb	RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
708174187Sdougb	ISC_LIST_INIT(contexts);
709193149Sdougb	totallost = 0;
710170222Sdougb}
711170222Sdougb
712135446Strhodes/*
713135446Strhodes * Public.
714135446Strhodes */
715135446Strhodes
716135446Strhodesisc_result_t
717135446Strhodesisc_mem_createx(size_t init_max_size, size_t target_size,
718135446Strhodes		isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
719135446Strhodes		isc_mem_t **ctxp)
720135446Strhodes{
721170222Sdougb	return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree,
722170222Sdougb				 arg, ctxp, ISC_MEMFLAG_DEFAULT));
723186462Sdougb
724170222Sdougb}
725170222Sdougb
726170222Sdougbisc_result_t
727170222Sdougbisc_mem_createx2(size_t init_max_size, size_t target_size,
728170222Sdougb		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
729170222Sdougb		 isc_mem_t **ctxp, unsigned int flags)
730170222Sdougb{
731135446Strhodes	isc_mem_t *ctx;
732135446Strhodes	isc_result_t result;
733135446Strhodes
734135446Strhodes	REQUIRE(ctxp != NULL && *ctxp == NULL);
735135446Strhodes	REQUIRE(memalloc != NULL);
736135446Strhodes	REQUIRE(memfree != NULL);
737135446Strhodes
738135446Strhodes	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);
739135446Strhodes
740170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
741135446Strhodes
742135446Strhodes	ctx = (memalloc)(arg, sizeof(*ctx));
743135446Strhodes	if (ctx == NULL)
744135446Strhodes		return (ISC_R_NOMEMORY);
745135446Strhodes
746170222Sdougb	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
747170222Sdougb		result = isc_mutex_init(&ctx->lock);
748170222Sdougb		if (result != ISC_R_SUCCESS) {
749170222Sdougb			(memfree)(arg, ctx);
750170222Sdougb			return (result);
751170222Sdougb		}
752153816Sdougb	}
753153816Sdougb
754135446Strhodes	if (init_max_size == 0U)
755135446Strhodes		ctx->max_size = DEF_MAX_SIZE;
756135446Strhodes	else
757135446Strhodes		ctx->max_size = init_max_size;
758170222Sdougb	ctx->flags = flags;
759135446Strhodes	ctx->references = 1;
760193149Sdougb	memset(ctx->name, 0, sizeof(ctx->name));
761193149Sdougb	ctx->tag = NULL;
762135446Strhodes	ctx->quota = 0;
763135446Strhodes	ctx->total = 0;
764135446Strhodes	ctx->inuse = 0;
765135446Strhodes	ctx->maxinuse = 0;
766135446Strhodes	ctx->hi_water = 0;
767135446Strhodes	ctx->lo_water = 0;
768135446Strhodes	ctx->hi_called = ISC_FALSE;
769214586Sdougb	ctx->is_overmem = ISC_FALSE;
770135446Strhodes	ctx->water = NULL;
771135446Strhodes	ctx->water_arg = NULL;
772135446Strhodes	ctx->magic = MEM_MAGIC;
773135446Strhodes	isc_ondestroy_init(&ctx->ondestroy);
774135446Strhodes	ctx->memalloc = memalloc;
775135446Strhodes	ctx->memfree = memfree;
776135446Strhodes	ctx->arg = arg;
777135446Strhodes	ctx->stats = NULL;
778135446Strhodes	ctx->checkfree = ISC_TRUE;
779135446Strhodes#if ISC_MEM_TRACKLINES
780135446Strhodes	ctx->debuglist = NULL;
781193149Sdougb	ctx->debuglistcnt = 0;
782135446Strhodes#endif
783135446Strhodes	ISC_LIST_INIT(ctx->pools);
784193149Sdougb	ctx->poolcnt = 0;
785135446Strhodes	ctx->freelists = NULL;
786170222Sdougb	ctx->basic_blocks = NULL;
787170222Sdougb	ctx->basic_table = NULL;
788170222Sdougb	ctx->basic_table_count = 0;
789170222Sdougb	ctx->basic_table_size = 0;
790170222Sdougb	ctx->lowest = NULL;
791170222Sdougb	ctx->highest = NULL;
792135446Strhodes
793135446Strhodes	ctx->stats = (memalloc)(arg,
794135446Strhodes				(ctx->max_size+1) * sizeof(struct stats));
795135446Strhodes	if (ctx->stats == NULL) {
796135446Strhodes		result = ISC_R_NOMEMORY;
797135446Strhodes		goto error;
798135446Strhodes	}
799135446Strhodes	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
800135446Strhodes
801170222Sdougb	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
802170222Sdougb		if (target_size == 0U)
803170222Sdougb			ctx->mem_target = DEF_MEM_TARGET;
804170222Sdougb		else
805170222Sdougb			ctx->mem_target = target_size;
806170222Sdougb		ctx->freelists = (memalloc)(arg, ctx->max_size *
807170222Sdougb						 sizeof(element *));
808170222Sdougb		if (ctx->freelists == NULL) {
809170222Sdougb			result = ISC_R_NOMEMORY;
810170222Sdougb			goto error;
811170222Sdougb		}
812170222Sdougb		memset(ctx->freelists, 0,
813170222Sdougb		       ctx->max_size * sizeof(element *));
814135446Strhodes	}
815135446Strhodes
816135446Strhodes#if ISC_MEM_TRACKLINES
817135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
818135446Strhodes		unsigned int i;
819135446Strhodes
820135446Strhodes		ctx->debuglist = (memalloc)(arg,
821135446Strhodes				      (ctx->max_size+1) * sizeof(debuglist_t));
822135446Strhodes		if (ctx->debuglist == NULL) {
823135446Strhodes			result = ISC_R_NOMEMORY;
824135446Strhodes			goto error;
825135446Strhodes		}
826135446Strhodes		for (i = 0; i <= ctx->max_size; i++)
827135446Strhodes			ISC_LIST_INIT(ctx->debuglist[i]);
828135446Strhodes	}
829135446Strhodes#endif
830135446Strhodes
831135446Strhodes	ctx->memalloc_failures = 0;
832135446Strhodes
833170222Sdougb	LOCK(&lock);
834170222Sdougb	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
835170222Sdougb	UNLOCK(&lock);
836170222Sdougb
837135446Strhodes	*ctxp = ctx;
838135446Strhodes	return (ISC_R_SUCCESS);
839135446Strhodes
840135446Strhodes  error:
841153816Sdougb	if (ctx != NULL) {
842153816Sdougb		if (ctx->stats != NULL)
843135446Strhodes			(memfree)(arg, ctx->stats);
844153816Sdougb		if (ctx->freelists != NULL)
845135446Strhodes			(memfree)(arg, ctx->freelists);
846135446Strhodes#if ISC_MEM_TRACKLINES
847153816Sdougb		if (ctx->debuglist != NULL)
848135446Strhodes			(ctx->memfree)(ctx->arg, ctx->debuglist);
849135446Strhodes#endif /* ISC_MEM_TRACKLINES */
850170222Sdougb		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
851170222Sdougb			DESTROYLOCK(&ctx->lock);
852135446Strhodes		(memfree)(arg, ctx);
853135446Strhodes	}
854135446Strhodes
855135446Strhodes	return (result);
856135446Strhodes}
857135446Strhodes
858135446Strhodesisc_result_t
859135446Strhodesisc_mem_create(size_t init_max_size, size_t target_size,
860135446Strhodes	       isc_mem_t **ctxp)
861135446Strhodes{
862170222Sdougb	return (isc_mem_createx2(init_max_size, target_size,
863170222Sdougb				 default_memalloc, default_memfree, NULL,
864170222Sdougb				 ctxp, ISC_MEMFLAG_DEFAULT));
865135446Strhodes}
866135446Strhodes
867170222Sdougbisc_result_t
868170222Sdougbisc_mem_create2(size_t init_max_size, size_t target_size,
869170222Sdougb		isc_mem_t **ctxp, unsigned int flags)
870170222Sdougb{
871170222Sdougb	return (isc_mem_createx2(init_max_size, target_size,
872170222Sdougb				 default_memalloc, default_memfree, NULL,
873170222Sdougb				 ctxp, flags));
874170222Sdougb}
875170222Sdougb
876135446Strhodesstatic void
877135446Strhodesdestroy(isc_mem_t *ctx) {
878135446Strhodes	unsigned int i;
879135446Strhodes	isc_ondestroy_t ondest;
880135446Strhodes
881170222Sdougb	LOCK(&lock);
882170222Sdougb	ISC_LIST_UNLINK(contexts, ctx, link);
883193149Sdougb	totallost += ctx->inuse;
884170222Sdougb	UNLOCK(&lock);
885170222Sdougb
886218384Sdougb	ctx->magic = 0;
887218384Sdougb
888135446Strhodes	INSIST(ISC_LIST_EMPTY(ctx->pools));
889135446Strhodes
890135446Strhodes#if ISC_MEM_TRACKLINES
891135446Strhodes	if (ctx->debuglist != NULL) {
892135446Strhodes		if (ctx->checkfree) {
893135446Strhodes			for (i = 0; i <= ctx->max_size; i++) {
894135446Strhodes				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
895135446Strhodes					print_active(ctx, stderr);
896135446Strhodes				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
897135446Strhodes			}
898135446Strhodes		} else {
899135446Strhodes			debuglink_t *dl;
900135446Strhodes
901135446Strhodes			for (i = 0; i <= ctx->max_size; i++)
902135446Strhodes				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
903135446Strhodes				     dl != NULL;
904135446Strhodes				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
905135446Strhodes					ISC_LIST_UNLINK(ctx->debuglist[i],
906186462Sdougb							dl, link);
907135446Strhodes					free(dl);
908135446Strhodes				}
909135446Strhodes		}
910135446Strhodes		(ctx->memfree)(ctx->arg, ctx->debuglist);
911135446Strhodes	}
912135446Strhodes#endif
913135446Strhodes	INSIST(ctx->references == 0);
914135446Strhodes
915135446Strhodes	if (ctx->checkfree) {
916135446Strhodes		for (i = 0; i <= ctx->max_size; i++) {
917135446Strhodes#if ISC_MEM_TRACKLINES
918135446Strhodes			if (ctx->stats[i].gets != 0U)
919135446Strhodes				print_active(ctx, stderr);
920135446Strhodes#endif
921135446Strhodes			INSIST(ctx->stats[i].gets == 0U);
922135446Strhodes		}
923135446Strhodes	}
924135446Strhodes
925135446Strhodes	(ctx->memfree)(ctx->arg, ctx->stats);
926135446Strhodes
927170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
928170222Sdougb		for (i = 0; i < ctx->basic_table_count; i++)
929170222Sdougb			(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
930170222Sdougb		(ctx->memfree)(ctx->arg, ctx->freelists);
931186462Sdougb		if (ctx->basic_table != NULL)
932186462Sdougb			(ctx->memfree)(ctx->arg, ctx->basic_table);
933170222Sdougb	}
934135446Strhodes
935135446Strhodes	ondest = ctx->ondestroy;
936135446Strhodes
937170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
938170222Sdougb		DESTROYLOCK(&ctx->lock);
939135446Strhodes	(ctx->memfree)(ctx->arg, ctx);
940135446Strhodes
941135446Strhodes	isc_ondestroy_notify(&ondest, ctx);
942135446Strhodes}
943135446Strhodes
944135446Strhodesvoid
945135446Strhodesisc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
946135446Strhodes	REQUIRE(VALID_CONTEXT(source));
947135446Strhodes	REQUIRE(targetp != NULL && *targetp == NULL);
948135446Strhodes
949170222Sdougb	MCTXLOCK(source, &source->lock);
950135446Strhodes	source->references++;
951170222Sdougb	MCTXUNLOCK(source, &source->lock);
952135446Strhodes
953135446Strhodes	*targetp = source;
954135446Strhodes}
955135446Strhodes
956135446Strhodesvoid
957135446Strhodesisc_mem_detach(isc_mem_t **ctxp) {
958135446Strhodes	isc_mem_t *ctx;
959135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
960135446Strhodes
961135446Strhodes	REQUIRE(ctxp != NULL);
962135446Strhodes	ctx = *ctxp;
963135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
964135446Strhodes
965170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
966135446Strhodes	INSIST(ctx->references > 0);
967135446Strhodes	ctx->references--;
968135446Strhodes	if (ctx->references == 0)
969135446Strhodes		want_destroy = ISC_TRUE;
970170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
971135446Strhodes
972135446Strhodes	if (want_destroy)
973135446Strhodes		destroy(ctx);
974135446Strhodes
975135446Strhodes	*ctxp = NULL;
976135446Strhodes}
977135446Strhodes
978135446Strhodes/*
979135446Strhodes * isc_mem_putanddetach() is the equivalent of:
980135446Strhodes *
981135446Strhodes * mctx = NULL;
982135446Strhodes * isc_mem_attach(ptr->mctx, &mctx);
983135446Strhodes * isc_mem_detach(&ptr->mctx);
984135446Strhodes * isc_mem_put(mctx, ptr, sizeof(*ptr);
985135446Strhodes * isc_mem_detach(&mctx);
986135446Strhodes */
987135446Strhodes
988135446Strhodesvoid
989135446Strhodesisc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
990135446Strhodes	isc_mem_t *ctx;
991135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
992170222Sdougb	size_info *si;
993170222Sdougb	size_t oldsize;
994135446Strhodes
995135446Strhodes	REQUIRE(ctxp != NULL);
996135446Strhodes	ctx = *ctxp;
997135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
998135446Strhodes	REQUIRE(ptr != NULL);
999135446Strhodes
1000135446Strhodes	/*
1001135446Strhodes	 * Must be before mem_putunlocked() as ctxp is usually within
1002135446Strhodes	 * [ptr..ptr+size).
1003135446Strhodes	 */
1004135446Strhodes	*ctxp = NULL;
1005135446Strhodes
1006170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1007170222Sdougb		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1008170222Sdougb			si = &(((size_info *)ptr)[-1]);
1009170222Sdougb			oldsize = si->u.size - ALIGNMENT_SIZE;
1010170222Sdougb			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1011170222Sdougb				oldsize -= ALIGNMENT_SIZE;
1012170222Sdougb			INSIST(oldsize == size);
1013170222Sdougb		}
1014170222Sdougb		isc__mem_free(ctx, ptr FLARG_PASS);
1015135446Strhodes
1016170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1017170222Sdougb		ctx->references--;
1018170222Sdougb		if (ctx->references == 0)
1019170222Sdougb			want_destroy = ISC_TRUE;
1020170222Sdougb		MCTXUNLOCK(ctx, &ctx->lock);
1021170222Sdougb		if (want_destroy)
1022170222Sdougb			destroy(ctx);
1023170222Sdougb
1024170222Sdougb		return;
1025170222Sdougb	}
1026170222Sdougb
1027170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1028170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1029170222Sdougb		mem_putunlocked(ctx, ptr, size);
1030170222Sdougb	} else {
1031170222Sdougb		mem_put(ctx, ptr, size);
1032170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1033170222Sdougb		mem_putstats(ctx, ptr, size);
1034170222Sdougb	}
1035170222Sdougb
1036135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1037135446Strhodes	INSIST(ctx->references > 0);
1038135446Strhodes	ctx->references--;
1039135446Strhodes	if (ctx->references == 0)
1040135446Strhodes		want_destroy = ISC_TRUE;
1041135446Strhodes
1042170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1043135446Strhodes
1044135446Strhodes	if (want_destroy)
1045135446Strhodes		destroy(ctx);
1046135446Strhodes}
1047135446Strhodes
1048135446Strhodesvoid
1049135446Strhodesisc_mem_destroy(isc_mem_t **ctxp) {
1050135446Strhodes	isc_mem_t *ctx;
1051135446Strhodes
1052135446Strhodes	/*
1053135446Strhodes	 * This routine provides legacy support for callers who use mctxs
1054135446Strhodes	 * without attaching/detaching.
1055135446Strhodes	 */
1056135446Strhodes
1057135446Strhodes	REQUIRE(ctxp != NULL);
1058135446Strhodes	ctx = *ctxp;
1059135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1060135446Strhodes
1061170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1062135446Strhodes#if ISC_MEM_TRACKLINES
1063135446Strhodes	if (ctx->references != 1)
1064135446Strhodes		print_active(ctx, stderr);
1065135446Strhodes#endif
1066135446Strhodes	REQUIRE(ctx->references == 1);
1067135446Strhodes	ctx->references--;
1068170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1069135446Strhodes
1070135446Strhodes	destroy(ctx);
1071135446Strhodes
1072135446Strhodes	*ctxp = NULL;
1073135446Strhodes}
1074135446Strhodes
1075135446Strhodesisc_result_t
1076135446Strhodesisc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) {
1077135446Strhodes	isc_result_t res;
1078135446Strhodes
1079170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1080135446Strhodes	res = isc_ondestroy_register(&ctx->ondestroy, task, event);
1081170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1082135446Strhodes
1083135446Strhodes	return (res);
1084135446Strhodes}
1085135446Strhodes
1086135446Strhodes
1087135446Strhodesvoid *
1088135446Strhodesisc__mem_get(isc_mem_t *ctx, size_t size FLARG) {
1089135446Strhodes	void *ptr;
1090135446Strhodes	isc_boolean_t call_water = ISC_FALSE;
1091135446Strhodes
1092135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1093135446Strhodes
1094170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
1095170222Sdougb		return (isc__mem_allocate(ctx, size FLARG_PASS));
1096135446Strhodes
1097170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1098170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1099170222Sdougb		ptr = mem_getunlocked(ctx, size);
1100170222Sdougb	} else {
1101170222Sdougb		ptr = mem_get(ctx, size);
1102170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1103170222Sdougb		if (ptr != NULL)
1104170222Sdougb			mem_getstats(ctx, size);
1105170222Sdougb	}
1106170222Sdougb
1107135446Strhodes	ADD_TRACE(ctx, ptr, size, file, line);
1108214586Sdougb	if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1109214586Sdougb	    !ctx->is_overmem) {
1110214586Sdougb		ctx->is_overmem = ISC_TRUE;
1111214586Sdougb	}
1112135446Strhodes	if (ctx->hi_water != 0U && !ctx->hi_called &&
1113135446Strhodes	    ctx->inuse > ctx->hi_water) {
1114135446Strhodes		call_water = ISC_TRUE;
1115135446Strhodes	}
1116135446Strhodes	if (ctx->inuse > ctx->maxinuse) {
1117135446Strhodes		ctx->maxinuse = ctx->inuse;
1118135446Strhodes		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1119135446Strhodes		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1120135446Strhodes			fprintf(stderr, "maxinuse = %lu\n",
1121135446Strhodes				(unsigned long)ctx->inuse);
1122135446Strhodes	}
1123170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1124135446Strhodes
1125135446Strhodes	if (call_water)
1126135446Strhodes		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1127135446Strhodes
1128135446Strhodes	return (ptr);
1129135446Strhodes}
1130135446Strhodes
1131135446Strhodesvoid
1132135446Strhodesisc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG)
1133135446Strhodes{
1134135446Strhodes	isc_boolean_t call_water = ISC_FALSE;
1135170222Sdougb	size_info *si;
1136170222Sdougb	size_t oldsize;
1137135446Strhodes
1138135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1139135446Strhodes	REQUIRE(ptr != NULL);
1140135446Strhodes
1141170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1142170222Sdougb		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1143170222Sdougb			si = &(((size_info *)ptr)[-1]);
1144170222Sdougb			oldsize = si->u.size - ALIGNMENT_SIZE;
1145170222Sdougb			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1146170222Sdougb				oldsize -= ALIGNMENT_SIZE;
1147170222Sdougb			INSIST(oldsize == size);
1148170222Sdougb		}
1149170222Sdougb		isc__mem_free(ctx, ptr FLARG_PASS);
1150170222Sdougb		return;
1151170222Sdougb	}
1152135446Strhodes
1153170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1154170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1155170222Sdougb		mem_putunlocked(ctx, ptr, size);
1156170222Sdougb	} else {
1157170222Sdougb		mem_put(ctx, ptr, size);
1158170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1159170222Sdougb		mem_putstats(ctx, ptr, size);
1160170222Sdougb	}
1161170222Sdougb
1162135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1163135446Strhodes
1164135446Strhodes	/*
1165135446Strhodes	 * The check against ctx->lo_water == 0 is for the condition
1166135446Strhodes	 * when the context was pushed over hi_water but then had
1167135446Strhodes	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1168135446Strhodes	 */
1169214586Sdougb	if (ctx->is_overmem &&
1170214586Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1171214586Sdougb		ctx->is_overmem = ISC_FALSE;
1172214586Sdougb	}
1173186462Sdougb	if (ctx->hi_called &&
1174135446Strhodes	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1175135446Strhodes		if (ctx->water != NULL)
1176135446Strhodes			call_water = ISC_TRUE;
1177135446Strhodes	}
1178170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1179135446Strhodes
1180135446Strhodes	if (call_water)
1181135446Strhodes		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1182135446Strhodes}
1183135446Strhodes
1184186462Sdougbvoid
1185186462Sdougbisc_mem_waterack(isc_mem_t *ctx, int flag) {
1186186462Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1187186462Sdougb
1188186462Sdougb	MCTXLOCK(ctx, &ctx->lock);
1189186462Sdougb	if (flag == ISC_MEM_LOWATER)
1190186462Sdougb		ctx->hi_called = ISC_FALSE;
1191186462Sdougb	else if (flag == ISC_MEM_HIWATER)
1192186462Sdougb		ctx->hi_called = ISC_TRUE;
1193186462Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1194186462Sdougb}
1195186462Sdougb
1196135446Strhodes#if ISC_MEM_TRACKLINES
1197135446Strhodesstatic void
1198135446Strhodesprint_active(isc_mem_t *mctx, FILE *out) {
1199135446Strhodes	if (mctx->debuglist != NULL) {
1200135446Strhodes		debuglink_t *dl;
1201135446Strhodes		unsigned int i, j;
1202135446Strhodes		const char *format;
1203135446Strhodes		isc_boolean_t found;
1204135446Strhodes
1205193149Sdougb		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1206135446Strhodes					    ISC_MSG_DUMPALLOC,
1207135446Strhodes					    "Dump of all outstanding "
1208135446Strhodes					    "memory allocations:\n"));
1209135446Strhodes		found = ISC_FALSE;
1210135446Strhodes		format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1211186462Sdougb					ISC_MSG_PTRFILELINE,
1212135446Strhodes					"\tptr %p size %u file %s line %u\n");
1213135446Strhodes		for (i = 0; i <= mctx->max_size; i++) {
1214135446Strhodes			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
1215186462Sdougb
1216135446Strhodes			if (dl != NULL)
1217135446Strhodes				found = ISC_TRUE;
1218135446Strhodes
1219135446Strhodes			while (dl != NULL) {
1220135446Strhodes				for (j = 0; j < DEBUGLIST_COUNT; j++)
1221135446Strhodes					if (dl->ptr[j] != NULL)
1222135446Strhodes						fprintf(out, format,
1223135446Strhodes							dl->ptr[j],
1224135446Strhodes							dl->size[j],
1225135446Strhodes							dl->file[j],
1226135446Strhodes							dl->line[j]);
1227135446Strhodes				dl = ISC_LIST_NEXT(dl, link);
1228135446Strhodes			}
1229135446Strhodes		}
1230135446Strhodes		if (!found)
1231193149Sdougb			fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1232135446Strhodes						    ISC_MSG_NONE, "\tNone.\n"));
1233135446Strhodes	}
1234135446Strhodes}
1235135446Strhodes#endif
1236135446Strhodes
1237135446Strhodes/*
1238135446Strhodes * Print the stats[] on the stream "out" with suitable formatting.
1239135446Strhodes */
1240135446Strhodesvoid
1241135446Strhodesisc_mem_stats(isc_mem_t *ctx, FILE *out) {
1242135446Strhodes	size_t i;
1243135446Strhodes	const struct stats *s;
1244135446Strhodes	const isc_mempool_t *pool;
1245135446Strhodes
1246135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1247170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1248135446Strhodes
1249135446Strhodes	for (i = 0; i <= ctx->max_size; i++) {
1250135446Strhodes		s = &ctx->stats[i];
1251135446Strhodes
1252135446Strhodes		if (s->totalgets == 0U && s->gets == 0U)
1253135446Strhodes			continue;
1254135446Strhodes		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
1255135446Strhodes			(i == ctx->max_size) ? ">=" : "  ",
1256135446Strhodes			(unsigned long) i, s->totalgets, s->gets);
1257170222Sdougb		if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
1258170222Sdougb		    (s->blocks != 0U || s->freefrags != 0U))
1259135446Strhodes			fprintf(out, " (%lu bl, %lu ff)",
1260135446Strhodes				s->blocks, s->freefrags);
1261135446Strhodes		fputc('\n', out);
1262135446Strhodes	}
1263135446Strhodes
1264135446Strhodes	/*
1265135446Strhodes	 * Note that since a pool can be locked now, these stats might be
1266135446Strhodes	 * somewhat off if the pool is in active use at the time the stats
1267135446Strhodes	 * are dumped.  The link fields are protected by the isc_mem_t's
1268135446Strhodes	 * lock, however, so walking this list and extracting integers from
1269135446Strhodes	 * stats fields is always safe.
1270135446Strhodes	 */
1271135446Strhodes	pool = ISC_LIST_HEAD(ctx->pools);
1272135446Strhodes	if (pool != NULL) {
1273193149Sdougb		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1274135446Strhodes					    ISC_MSG_POOLSTATS,
1275135446Strhodes					    "[Pool statistics]\n"));
1276135446Strhodes		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
1277135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1278135446Strhodes				       ISC_MSG_POOLNAME, "name"),
1279135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1280135446Strhodes				       ISC_MSG_POOLSIZE, "size"),
1281135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1282135446Strhodes				       ISC_MSG_POOLMAXALLOC, "maxalloc"),
1283135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1284135446Strhodes				       ISC_MSG_POOLALLOCATED, "allocated"),
1285135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1286135446Strhodes				       ISC_MSG_POOLFREECOUNT, "freecount"),
1287135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1288135446Strhodes				       ISC_MSG_POOLFREEMAX, "freemax"),
1289135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1290135446Strhodes				       ISC_MSG_POOLFILLCOUNT, "fillcount"),
1291135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1292135446Strhodes				       ISC_MSG_POOLGETS, "gets"),
1293135446Strhodes			"L");
1294135446Strhodes	}
1295135446Strhodes	while (pool != NULL) {
1296135446Strhodes		fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
1297135446Strhodes			pool->name, (unsigned long) pool->size, pool->maxalloc,
1298135446Strhodes			pool->allocated, pool->freecount, pool->freemax,
1299135446Strhodes			pool->fillcount, pool->gets,
1300135446Strhodes			(pool->lock == NULL ? "N" : "Y"));
1301135446Strhodes		pool = ISC_LIST_NEXT(pool, link);
1302135446Strhodes	}
1303135446Strhodes
1304135446Strhodes#if ISC_MEM_TRACKLINES
1305135446Strhodes	print_active(ctx, out);
1306135446Strhodes#endif
1307135446Strhodes
1308170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1309135446Strhodes}
1310135446Strhodes
1311135446Strhodes/*
1312135446Strhodes * Replacements for malloc() and free() -- they implicitly remember the
1313135446Strhodes * size of the object allocated (with some additional overhead).
1314135446Strhodes */
1315135446Strhodes
1316135446Strhodesstatic void *
1317135446Strhodesisc__mem_allocateunlocked(isc_mem_t *ctx, size_t size) {
1318135446Strhodes	size_info *si;
1319135446Strhodes
1320135446Strhodes	size += ALIGNMENT_SIZE;
1321170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1322170222Sdougb		size += ALIGNMENT_SIZE;
1323170222Sdougb
1324170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
1325170222Sdougb		si = mem_getunlocked(ctx, size);
1326170222Sdougb	else
1327170222Sdougb		si = mem_get(ctx, size);
1328170222Sdougb
1329135446Strhodes	if (si == NULL)
1330135446Strhodes		return (NULL);
1331170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1332170222Sdougb		si->u.ctx = ctx;
1333170222Sdougb		si++;
1334170222Sdougb	}
1335135446Strhodes	si->u.size = size;
1336135446Strhodes	return (&si[1]);
1337135446Strhodes}
1338135446Strhodes
1339135446Strhodesvoid *
1340135446Strhodesisc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
1341135446Strhodes	size_info *si;
1342170222Sdougb	isc_boolean_t call_water = ISC_FALSE;
1343135446Strhodes
1344135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1345135446Strhodes
1346170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1347170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1348170222Sdougb		si = isc__mem_allocateunlocked(ctx, size);
1349170222Sdougb	} else {
1350170222Sdougb		si = isc__mem_allocateunlocked(ctx, size);
1351170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1352170222Sdougb		if (si != NULL)
1353170222Sdougb			mem_getstats(ctx, si[-1].u.size);
1354170222Sdougb	}
1355135446Strhodes
1356135446Strhodes#if ISC_MEM_TRACKLINES
1357135446Strhodes	ADD_TRACE(ctx, si, si[-1].u.size, file, line);
1358135446Strhodes#endif
1359214586Sdougb	if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1360214586Sdougb	    !ctx->is_overmem) {
1361214586Sdougb		ctx->is_overmem = ISC_TRUE;
1362214586Sdougb	}
1363214586Sdougb
1364170222Sdougb	if (ctx->hi_water != 0U && !ctx->hi_called &&
1365170222Sdougb	    ctx->inuse > ctx->hi_water) {
1366170222Sdougb		ctx->hi_called = ISC_TRUE;
1367170222Sdougb		call_water = ISC_TRUE;
1368170222Sdougb	}
1369170222Sdougb	if (ctx->inuse > ctx->maxinuse) {
1370170222Sdougb		ctx->maxinuse = ctx->inuse;
1371170222Sdougb		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1372170222Sdougb		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1373170222Sdougb			fprintf(stderr, "maxinuse = %lu\n",
1374170222Sdougb				(unsigned long)ctx->inuse);
1375170222Sdougb	}
1376170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1377135446Strhodes
1378170222Sdougb	if (call_water)
1379170222Sdougb		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1380135446Strhodes
1381135446Strhodes	return (si);
1382135446Strhodes}
1383135446Strhodes
1384193149Sdougbvoid *
1385193149Sdougbisc__mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG) {
1386193149Sdougb	void *new_ptr = NULL;
1387193149Sdougb	size_t oldsize, copysize;
1388193149Sdougb
1389193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1390193149Sdougb
1391193149Sdougb	/*
1392193149Sdougb	 * This function emulates the realloc(3) standard library function:
1393193149Sdougb	 * - if size > 0, allocate new memory; and if ptr is non NULL, copy
1394193149Sdougb	 *   as much of the old contents to the new buffer and free the old one.
1395193149Sdougb	 *   Note that when allocation fails the original pointer is intact;
1396193149Sdougb	 *   the caller must free it.
1397193149Sdougb	 * - if size is 0 and ptr is non NULL, simply free the given ptr.
1398193149Sdougb	 * - this function returns:
1399193149Sdougb	 *     pointer to the newly allocated memory, or
1400193149Sdougb	 *     NULL if allocation fails or doesn't happen.
1401193149Sdougb	 */
1402193149Sdougb	if (size > 0U) {
1403193149Sdougb		new_ptr = isc__mem_allocate(ctx, size FLARG_PASS);
1404193149Sdougb		if (new_ptr != NULL && ptr != NULL) {
1405193149Sdougb			oldsize = (((size_info *)ptr)[-1]).u.size;
1406193149Sdougb			INSIST(oldsize >= ALIGNMENT_SIZE);
1407193149Sdougb			oldsize -= ALIGNMENT_SIZE;
1408193149Sdougb			copysize = oldsize > size ? size : oldsize;
1409193149Sdougb			memcpy(new_ptr, ptr, copysize);
1410193149Sdougb			isc__mem_free(ctx, ptr FLARG_PASS);
1411193149Sdougb		}
1412193149Sdougb	} else if (ptr != NULL)
1413193149Sdougb		isc__mem_free(ctx, ptr FLARG_PASS);
1414193149Sdougb
1415193149Sdougb	return (new_ptr);
1416193149Sdougb}
1417193149Sdougb
1418135446Strhodesvoid
1419135446Strhodesisc__mem_free(isc_mem_t *ctx, void *ptr FLARG) {
1420135446Strhodes	size_info *si;
1421135446Strhodes	size_t size;
1422170222Sdougb	isc_boolean_t call_water= ISC_FALSE;
1423135446Strhodes
1424135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1425135446Strhodes	REQUIRE(ptr != NULL);
1426135446Strhodes
1427170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1428170222Sdougb		si = &(((size_info *)ptr)[-2]);
1429170222Sdougb		REQUIRE(si->u.ctx == ctx);
1430170222Sdougb		size = si[1].u.size;
1431170222Sdougb	} else {
1432170222Sdougb		si = &(((size_info *)ptr)[-1]);
1433170222Sdougb		size = si->u.size;
1434170222Sdougb	}
1435135446Strhodes
1436170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1437170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1438170222Sdougb		mem_putunlocked(ctx, si, size);
1439170222Sdougb	} else {
1440170222Sdougb		mem_put(ctx, si, size);
1441170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1442170222Sdougb		mem_putstats(ctx, si, size);
1443170222Sdougb	}
1444135446Strhodes
1445135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1446135446Strhodes
1447170222Sdougb	/*
1448170222Sdougb	 * The check against ctx->lo_water == 0 is for the condition
1449170222Sdougb	 * when the context was pushed over hi_water but then had
1450170222Sdougb	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1451170222Sdougb	 */
1452214586Sdougb	if (ctx->is_overmem &&
1453214586Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1454214586Sdougb		ctx->is_overmem = ISC_FALSE;
1455214586Sdougb	}
1456214586Sdougb
1457186462Sdougb	if (ctx->hi_called &&
1458170222Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1459170222Sdougb		ctx->hi_called = ISC_FALSE;
1460170222Sdougb
1461170222Sdougb		if (ctx->water != NULL)
1462170222Sdougb			call_water = ISC_TRUE;
1463170222Sdougb	}
1464170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1465170222Sdougb
1466170222Sdougb	if (call_water)
1467170222Sdougb		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1468135446Strhodes}
1469135446Strhodes
1470135446Strhodes
1471135446Strhodes/*
1472135446Strhodes * Other useful things.
1473135446Strhodes */
1474135446Strhodes
1475135446Strhodeschar *
1476135446Strhodesisc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
1477135446Strhodes	size_t len;
1478135446Strhodes	char *ns;
1479135446Strhodes
1480135446Strhodes	REQUIRE(VALID_CONTEXT(mctx));
1481135446Strhodes	REQUIRE(s != NULL);
1482135446Strhodes
1483135446Strhodes	len = strlen(s);
1484135446Strhodes
1485135446Strhodes	ns = isc__mem_allocate(mctx, len + 1 FLARG_PASS);
1486135446Strhodes
1487135446Strhodes	if (ns != NULL)
1488135446Strhodes		strncpy(ns, s, len + 1);
1489135446Strhodes
1490135446Strhodes	return (ns);
1491135446Strhodes}
1492135446Strhodes
1493135446Strhodesvoid
1494135446Strhodesisc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) {
1495135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1496170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1497135446Strhodes
1498135446Strhodes	ctx->checkfree = flag;
1499135446Strhodes
1500170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1501135446Strhodes}
1502135446Strhodes
1503135446Strhodes/*
1504135446Strhodes * Quotas
1505135446Strhodes */
1506135446Strhodes
1507135446Strhodesvoid
1508135446Strhodesisc_mem_setquota(isc_mem_t *ctx, size_t quota) {
1509135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1510170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1511135446Strhodes
1512135446Strhodes	ctx->quota = quota;
1513135446Strhodes
1514170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1515135446Strhodes}
1516135446Strhodes
1517135446Strhodessize_t
1518135446Strhodesisc_mem_getquota(isc_mem_t *ctx) {
1519135446Strhodes	size_t quota;
1520135446Strhodes
1521135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1522170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1523135446Strhodes
1524135446Strhodes	quota = ctx->quota;
1525135446Strhodes
1526170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1527135446Strhodes
1528135446Strhodes	return (quota);
1529135446Strhodes}
1530135446Strhodes
1531135446Strhodessize_t
1532135446Strhodesisc_mem_inuse(isc_mem_t *ctx) {
1533135446Strhodes	size_t inuse;
1534135446Strhodes
1535135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1536170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1537135446Strhodes
1538135446Strhodes	inuse = ctx->inuse;
1539135446Strhodes
1540170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1541135446Strhodes
1542135446Strhodes	return (inuse);
1543135446Strhodes}
1544135446Strhodes
1545135446Strhodesvoid
1546135446Strhodesisc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
1547186462Sdougb		 size_t hiwater, size_t lowater)
1548135446Strhodes{
1549170222Sdougb	isc_boolean_t callwater = ISC_FALSE;
1550170222Sdougb	isc_mem_water_t oldwater;
1551170222Sdougb	void *oldwater_arg;
1552170222Sdougb
1553135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1554135446Strhodes	REQUIRE(hiwater >= lowater);
1555135446Strhodes
1556170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1557170222Sdougb	oldwater = ctx->water;
1558170222Sdougb	oldwater_arg = ctx->water_arg;
1559135446Strhodes	if (water == NULL) {
1560170222Sdougb		callwater = ctx->hi_called;
1561135446Strhodes		ctx->water = NULL;
1562135446Strhodes		ctx->water_arg = NULL;
1563135446Strhodes		ctx->hi_water = 0;
1564135446Strhodes		ctx->lo_water = 0;
1565135446Strhodes		ctx->hi_called = ISC_FALSE;
1566135446Strhodes	} else {
1567170222Sdougb		if (ctx->hi_called &&
1568170222Sdougb		    (ctx->water != water || ctx->water_arg != water_arg ||
1569170222Sdougb		     ctx->inuse < lowater || lowater == 0U))
1570170222Sdougb			callwater = ISC_TRUE;
1571135446Strhodes		ctx->water = water;
1572135446Strhodes		ctx->water_arg = water_arg;
1573135446Strhodes		ctx->hi_water = hiwater;
1574135446Strhodes		ctx->lo_water = lowater;
1575135446Strhodes		ctx->hi_called = ISC_FALSE;
1576135446Strhodes	}
1577170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1578186462Sdougb
1579170222Sdougb	if (callwater && oldwater != NULL)
1580170222Sdougb		(oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1581135446Strhodes}
1582135446Strhodes
1583214586Sdougbisc_boolean_t
1584214586Sdougbisc_mem_isovermem(isc_mem_t *ctx) {
1585214586Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1586214586Sdougb
1587214586Sdougb	/*
1588214586Sdougb	 * We don't bother to lock the context because 100% accuracy isn't
1589214586Sdougb	 * necessary (and even if we locked the context the returned value
1590214586Sdougb	 * could be different from the actual state when it's used anyway)
1591214586Sdougb	 */
1592214586Sdougb	return (ctx->is_overmem);
1593214586Sdougb}
1594214586Sdougb
1595193149Sdougbvoid
1596193149Sdougbisc_mem_setname(isc_mem_t *ctx, const char *name, void *tag) {
1597193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1598193149Sdougb
1599193149Sdougb	LOCK(&ctx->lock);
1600193149Sdougb	memset(ctx->name, 0, sizeof(ctx->name));
1601193149Sdougb	strncpy(ctx->name, name, sizeof(ctx->name) - 1);
1602193149Sdougb	ctx->tag = tag;
1603193149Sdougb	UNLOCK(&ctx->lock);
1604193149Sdougb}
1605193149Sdougb
1606193149Sdougbconst char *
1607193149Sdougbisc_mem_getname(isc_mem_t *ctx) {
1608193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1609193149Sdougb
1610193149Sdougb	return (ctx->name);
1611193149Sdougb}
1612193149Sdougb
1613193149Sdougbvoid *
1614193149Sdougbisc_mem_gettag(isc_mem_t *ctx) {
1615193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1616193149Sdougb
1617193149Sdougb	return (ctx->tag);
1618193149Sdougb}
1619193149Sdougb
1620135446Strhodes/*
1621135446Strhodes * Memory pool stuff
1622135446Strhodes */
1623135446Strhodes
1624135446Strhodesisc_result_t
1625135446Strhodesisc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) {
1626135446Strhodes	isc_mempool_t *mpctx;
1627135446Strhodes
1628135446Strhodes	REQUIRE(VALID_CONTEXT(mctx));
1629135446Strhodes	REQUIRE(size > 0U);
1630135446Strhodes	REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1631135446Strhodes
1632135446Strhodes	/*
1633135446Strhodes	 * Allocate space for this pool, initialize values, and if all works
1634135446Strhodes	 * well, attach to the memory context.
1635135446Strhodes	 */
1636135446Strhodes	mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
1637135446Strhodes	if (mpctx == NULL)
1638135446Strhodes		return (ISC_R_NOMEMORY);
1639135446Strhodes
1640135446Strhodes	mpctx->magic = MEMPOOL_MAGIC;
1641135446Strhodes	mpctx->lock = NULL;
1642135446Strhodes	mpctx->mctx = mctx;
1643135446Strhodes	mpctx->size = size;
1644135446Strhodes	mpctx->maxalloc = UINT_MAX;
1645135446Strhodes	mpctx->allocated = 0;
1646135446Strhodes	mpctx->freecount = 0;
1647135446Strhodes	mpctx->freemax = 1;
1648135446Strhodes	mpctx->fillcount = 1;
1649135446Strhodes	mpctx->gets = 0;
1650135446Strhodes#if ISC_MEMPOOL_NAMES
1651135446Strhodes	mpctx->name[0] = 0;
1652135446Strhodes#endif
1653135446Strhodes	mpctx->items = NULL;
1654135446Strhodes
1655135446Strhodes	*mpctxp = mpctx;
1656135446Strhodes
1657170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1658135446Strhodes	ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1659193149Sdougb	mctx->poolcnt++;
1660170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1661135446Strhodes
1662135446Strhodes	return (ISC_R_SUCCESS);
1663135446Strhodes}
1664135446Strhodes
1665135446Strhodesvoid
1666135446Strhodesisc_mempool_setname(isc_mempool_t *mpctx, const char *name) {
1667135446Strhodes	REQUIRE(name != NULL);
1668135446Strhodes
1669135446Strhodes#if ISC_MEMPOOL_NAMES
1670135446Strhodes	if (mpctx->lock != NULL)
1671135446Strhodes		LOCK(mpctx->lock);
1672135446Strhodes
1673135446Strhodes	strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
1674135446Strhodes	mpctx->name[sizeof(mpctx->name) - 1] = '\0';
1675135446Strhodes
1676135446Strhodes	if (mpctx->lock != NULL)
1677135446Strhodes		UNLOCK(mpctx->lock);
1678135446Strhodes#else
1679135446Strhodes	UNUSED(mpctx);
1680135446Strhodes	UNUSED(name);
1681135446Strhodes#endif
1682135446Strhodes}
1683135446Strhodes
1684135446Strhodesvoid
1685135446Strhodesisc_mempool_destroy(isc_mempool_t **mpctxp) {
1686135446Strhodes	isc_mempool_t *mpctx;
1687135446Strhodes	isc_mem_t *mctx;
1688135446Strhodes	isc_mutex_t *lock;
1689135446Strhodes	element *item;
1690135446Strhodes
1691135446Strhodes	REQUIRE(mpctxp != NULL);
1692135446Strhodes	mpctx = *mpctxp;
1693135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1694135446Strhodes#if ISC_MEMPOOL_NAMES
1695135446Strhodes	if (mpctx->allocated > 0)
1696135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
1697135446Strhodes				 "isc_mempool_destroy(): mempool %s "
1698135446Strhodes				 "leaked memory",
1699135446Strhodes				 mpctx->name);
1700135446Strhodes#endif
1701135446Strhodes	REQUIRE(mpctx->allocated == 0);
1702135446Strhodes
1703135446Strhodes	mctx = mpctx->mctx;
1704135446Strhodes
1705135446Strhodes	lock = mpctx->lock;
1706135446Strhodes
1707135446Strhodes	if (lock != NULL)
1708135446Strhodes		LOCK(lock);
1709135446Strhodes
1710135446Strhodes	/*
1711135446Strhodes	 * Return any items on the free list
1712135446Strhodes	 */
1713170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1714135446Strhodes	while (mpctx->items != NULL) {
1715135446Strhodes		INSIST(mpctx->freecount > 0);
1716135446Strhodes		mpctx->freecount--;
1717135446Strhodes		item = mpctx->items;
1718135446Strhodes		mpctx->items = item->next;
1719135446Strhodes
1720170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1721170222Sdougb			mem_putunlocked(mctx, item, mpctx->size);
1722170222Sdougb		} else {
1723170222Sdougb			mem_put(mctx, item, mpctx->size);
1724170222Sdougb			mem_putstats(mctx, item, mpctx->size);
1725170222Sdougb		}
1726135446Strhodes	}
1727170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1728135446Strhodes
1729135446Strhodes	/*
1730135446Strhodes	 * Remove our linked list entry from the memory context.
1731135446Strhodes	 */
1732170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1733135446Strhodes	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1734193149Sdougb	mctx->poolcnt--;
1735170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1736135446Strhodes
1737135446Strhodes	mpctx->magic = 0;
1738135446Strhodes
1739135446Strhodes	isc_mem_put(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
1740135446Strhodes
1741135446Strhodes	if (lock != NULL)
1742135446Strhodes		UNLOCK(lock);
1743135446Strhodes
1744135446Strhodes	*mpctxp = NULL;
1745135446Strhodes}
1746135446Strhodes
1747135446Strhodesvoid
1748135446Strhodesisc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock) {
1749135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1750135446Strhodes	REQUIRE(mpctx->lock == NULL);
1751135446Strhodes	REQUIRE(lock != NULL);
1752135446Strhodes
1753135446Strhodes	mpctx->lock = lock;
1754135446Strhodes}
1755135446Strhodes
1756135446Strhodesvoid *
1757135446Strhodesisc__mempool_get(isc_mempool_t *mpctx FLARG) {
1758135446Strhodes	element *item;
1759135446Strhodes	isc_mem_t *mctx;
1760135446Strhodes	unsigned int i;
1761135446Strhodes
1762135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1763135446Strhodes
1764135446Strhodes	mctx = mpctx->mctx;
1765135446Strhodes
1766135446Strhodes	if (mpctx->lock != NULL)
1767135446Strhodes		LOCK(mpctx->lock);
1768135446Strhodes
1769135446Strhodes	/*
1770135446Strhodes	 * Don't let the caller go over quota
1771135446Strhodes	 */
1772135446Strhodes	if (mpctx->allocated >= mpctx->maxalloc) {
1773135446Strhodes		item = NULL;
1774135446Strhodes		goto out;
1775135446Strhodes	}
1776135446Strhodes
1777135446Strhodes	/*
1778135446Strhodes	 * if we have a free list item, return the first here
1779135446Strhodes	 */
1780135446Strhodes	item = mpctx->items;
1781135446Strhodes	if (item != NULL) {
1782135446Strhodes		mpctx->items = item->next;
1783135446Strhodes		INSIST(mpctx->freecount > 0);
1784135446Strhodes		mpctx->freecount--;
1785135446Strhodes		mpctx->gets++;
1786135446Strhodes		mpctx->allocated++;
1787135446Strhodes		goto out;
1788135446Strhodes	}
1789135446Strhodes
1790135446Strhodes	/*
1791135446Strhodes	 * We need to dip into the well.  Lock the memory context here and
1792135446Strhodes	 * fill up our free list.
1793135446Strhodes	 */
1794170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1795135446Strhodes	for (i = 0; i < mpctx->fillcount; i++) {
1796170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1797170222Sdougb			item = mem_getunlocked(mctx, mpctx->size);
1798170222Sdougb		} else {
1799170222Sdougb			item = mem_get(mctx, mpctx->size);
1800170222Sdougb			if (item != NULL)
1801170222Sdougb				mem_getstats(mctx, mpctx->size);
1802170222Sdougb		}
1803135446Strhodes		if (item == NULL)
1804135446Strhodes			break;
1805135446Strhodes		item->next = mpctx->items;
1806135446Strhodes		mpctx->items = item;
1807135446Strhodes		mpctx->freecount++;
1808135446Strhodes	}
1809170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1810135446Strhodes
1811135446Strhodes	/*
1812135446Strhodes	 * If we didn't get any items, return NULL.
1813135446Strhodes	 */
1814135446Strhodes	item = mpctx->items;
1815135446Strhodes	if (item == NULL)
1816135446Strhodes		goto out;
1817135446Strhodes
1818135446Strhodes	mpctx->items = item->next;
1819135446Strhodes	mpctx->freecount--;
1820135446Strhodes	mpctx->gets++;
1821135446Strhodes	mpctx->allocated++;
1822135446Strhodes
1823135446Strhodes out:
1824135446Strhodes	if (mpctx->lock != NULL)
1825135446Strhodes		UNLOCK(mpctx->lock);
1826135446Strhodes
1827135446Strhodes#if ISC_MEM_TRACKLINES
1828135446Strhodes	if (item != NULL) {
1829170222Sdougb		MCTXLOCK(mctx, &mctx->lock);
1830135446Strhodes		ADD_TRACE(mctx, item, mpctx->size, file, line);
1831170222Sdougb		MCTXUNLOCK(mctx, &mctx->lock);
1832135446Strhodes	}
1833135446Strhodes#endif /* ISC_MEM_TRACKLINES */
1834135446Strhodes
1835135446Strhodes	return (item);
1836135446Strhodes}
1837135446Strhodes
1838135446Strhodesvoid
1839135446Strhodesisc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) {
1840135446Strhodes	isc_mem_t *mctx;
1841135446Strhodes	element *item;
1842135446Strhodes
1843135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1844135446Strhodes	REQUIRE(mem != NULL);
1845135446Strhodes
1846135446Strhodes	mctx = mpctx->mctx;
1847135446Strhodes
1848135446Strhodes	if (mpctx->lock != NULL)
1849135446Strhodes		LOCK(mpctx->lock);
1850135446Strhodes
1851135446Strhodes	INSIST(mpctx->allocated > 0);
1852135446Strhodes	mpctx->allocated--;
1853135446Strhodes
1854135446Strhodes#if ISC_MEM_TRACKLINES
1855170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1856135446Strhodes	DELETE_TRACE(mctx, mem, mpctx->size, file, line);
1857170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1858135446Strhodes#endif /* ISC_MEM_TRACKLINES */
1859135446Strhodes
1860135446Strhodes	/*
1861135446Strhodes	 * If our free list is full, return this to the mctx directly.
1862135446Strhodes	 */
1863135446Strhodes	if (mpctx->freecount >= mpctx->freemax) {
1864170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1865170222Sdougb			MCTXLOCK(mctx, &mctx->lock);
1866170222Sdougb			mem_putunlocked(mctx, mem, mpctx->size);
1867170222Sdougb			MCTXUNLOCK(mctx, &mctx->lock);
1868170222Sdougb		} else {
1869170222Sdougb			mem_put(mctx, mem, mpctx->size);
1870170222Sdougb			MCTXLOCK(mctx, &mctx->lock);
1871170222Sdougb			mem_putstats(mctx, mem, mpctx->size);
1872170222Sdougb			MCTXUNLOCK(mctx, &mctx->lock);
1873170222Sdougb		}
1874135446Strhodes		if (mpctx->lock != NULL)
1875135446Strhodes			UNLOCK(mpctx->lock);
1876135446Strhodes		return;
1877135446Strhodes	}
1878135446Strhodes
1879135446Strhodes	/*
1880135446Strhodes	 * Otherwise, attach it to our free list and bump the counter.
1881135446Strhodes	 */
1882135446Strhodes	mpctx->freecount++;
1883135446Strhodes	item = (element *)mem;
1884135446Strhodes	item->next = mpctx->items;
1885135446Strhodes	mpctx->items = item;
1886135446Strhodes
1887135446Strhodes	if (mpctx->lock != NULL)
1888135446Strhodes		UNLOCK(mpctx->lock);
1889135446Strhodes}
1890135446Strhodes
1891135446Strhodes/*
1892135446Strhodes * Quotas
1893135446Strhodes */
1894135446Strhodes
1895135446Strhodesvoid
1896135446Strhodesisc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit) {
1897135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1898135446Strhodes
1899135446Strhodes	if (mpctx->lock != NULL)
1900135446Strhodes		LOCK(mpctx->lock);
1901135446Strhodes
1902135446Strhodes	mpctx->freemax = limit;
1903135446Strhodes
1904135446Strhodes	if (mpctx->lock != NULL)
1905135446Strhodes		UNLOCK(mpctx->lock);
1906135446Strhodes}
1907135446Strhodes
1908135446Strhodesunsigned int
1909135446Strhodesisc_mempool_getfreemax(isc_mempool_t *mpctx) {
1910135446Strhodes	unsigned int freemax;
1911135446Strhodes
1912135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1913135446Strhodes
1914135446Strhodes	if (mpctx->lock != NULL)
1915135446Strhodes		LOCK(mpctx->lock);
1916135446Strhodes
1917135446Strhodes	freemax = mpctx->freemax;
1918135446Strhodes
1919135446Strhodes	if (mpctx->lock != NULL)
1920135446Strhodes		UNLOCK(mpctx->lock);
1921135446Strhodes
1922135446Strhodes	return (freemax);
1923135446Strhodes}
1924135446Strhodes
1925135446Strhodesunsigned int
1926135446Strhodesisc_mempool_getfreecount(isc_mempool_t *mpctx) {
1927135446Strhodes	unsigned int freecount;
1928135446Strhodes
1929135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1930135446Strhodes
1931135446Strhodes	if (mpctx->lock != NULL)
1932135446Strhodes		LOCK(mpctx->lock);
1933135446Strhodes
1934135446Strhodes	freecount = mpctx->freecount;
1935135446Strhodes
1936135446Strhodes	if (mpctx->lock != NULL)
1937135446Strhodes		UNLOCK(mpctx->lock);
1938135446Strhodes
1939135446Strhodes	return (freecount);
1940135446Strhodes}
1941135446Strhodes
1942135446Strhodesvoid
1943135446Strhodesisc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit) {
1944135446Strhodes	REQUIRE(limit > 0);
1945135446Strhodes
1946135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1947135446Strhodes
1948135446Strhodes	if (mpctx->lock != NULL)
1949135446Strhodes		LOCK(mpctx->lock);
1950135446Strhodes
1951135446Strhodes	mpctx->maxalloc = limit;
1952135446Strhodes
1953135446Strhodes	if (mpctx->lock != NULL)
1954135446Strhodes		UNLOCK(mpctx->lock);
1955135446Strhodes}
1956135446Strhodes
1957135446Strhodesunsigned int
1958135446Strhodesisc_mempool_getmaxalloc(isc_mempool_t *mpctx) {
1959135446Strhodes	unsigned int maxalloc;
1960135446Strhodes
1961135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1962135446Strhodes
1963135446Strhodes	if (mpctx->lock != NULL)
1964135446Strhodes		LOCK(mpctx->lock);
1965135446Strhodes
1966135446Strhodes	maxalloc = mpctx->maxalloc;
1967135446Strhodes
1968135446Strhodes	if (mpctx->lock != NULL)
1969135446Strhodes		UNLOCK(mpctx->lock);
1970135446Strhodes
1971135446Strhodes	return (maxalloc);
1972135446Strhodes}
1973135446Strhodes
1974135446Strhodesunsigned int
1975135446Strhodesisc_mempool_getallocated(isc_mempool_t *mpctx) {
1976135446Strhodes	unsigned int allocated;
1977135446Strhodes
1978135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1979135446Strhodes
1980135446Strhodes	if (mpctx->lock != NULL)
1981135446Strhodes		LOCK(mpctx->lock);
1982135446Strhodes
1983135446Strhodes	allocated = mpctx->allocated;
1984135446Strhodes
1985135446Strhodes	if (mpctx->lock != NULL)
1986135446Strhodes		UNLOCK(mpctx->lock);
1987135446Strhodes
1988135446Strhodes	return (allocated);
1989135446Strhodes}
1990135446Strhodes
1991135446Strhodesvoid
1992135446Strhodesisc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit) {
1993135446Strhodes	REQUIRE(limit > 0);
1994135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1995135446Strhodes
1996135446Strhodes	if (mpctx->lock != NULL)
1997135446Strhodes		LOCK(mpctx->lock);
1998135446Strhodes
1999135446Strhodes	mpctx->fillcount = limit;
2000135446Strhodes
2001135446Strhodes	if (mpctx->lock != NULL)
2002135446Strhodes		UNLOCK(mpctx->lock);
2003135446Strhodes}
2004135446Strhodes
2005135446Strhodesunsigned int
2006135446Strhodesisc_mempool_getfillcount(isc_mempool_t *mpctx) {
2007135446Strhodes	unsigned int fillcount;
2008135446Strhodes
2009135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2010135446Strhodes
2011135446Strhodes	if (mpctx->lock != NULL)
2012135446Strhodes		LOCK(mpctx->lock);
2013135446Strhodes
2014135446Strhodes	fillcount = mpctx->fillcount;
2015135446Strhodes
2016135446Strhodes	if (mpctx->lock != NULL)
2017135446Strhodes		UNLOCK(mpctx->lock);
2018135446Strhodes
2019135446Strhodes	return (fillcount);
2020135446Strhodes}
2021170222Sdougb
2022170222Sdougbvoid
2023170222Sdougbisc_mem_printactive(isc_mem_t *ctx, FILE *file) {
2024170222Sdougb
2025170222Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2026170222Sdougb	REQUIRE(file != NULL);
2027170222Sdougb
2028170222Sdougb#if !ISC_MEM_TRACKLINES
2029170222Sdougb	UNUSED(ctx);
2030170222Sdougb	UNUSED(file);
2031170222Sdougb#else
2032170222Sdougb	print_active(ctx, file);
2033170222Sdougb#endif
2034170222Sdougb}
2035170222Sdougb
2036170222Sdougbvoid
2037170222Sdougbisc_mem_printallactive(FILE *file) {
2038170222Sdougb#if !ISC_MEM_TRACKLINES
2039170222Sdougb	UNUSED(file);
2040170222Sdougb#else
2041170222Sdougb	isc_mem_t *ctx;
2042170222Sdougb
2043170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2044170222Sdougb
2045170222Sdougb	LOCK(&lock);
2046170222Sdougb	for (ctx = ISC_LIST_HEAD(contexts);
2047170222Sdougb	     ctx != NULL;
2048170222Sdougb	     ctx = ISC_LIST_NEXT(ctx, link)) {
2049170222Sdougb		fprintf(file, "context: %p\n", ctx);
2050170222Sdougb		print_active(ctx, file);
2051170222Sdougb	}
2052170222Sdougb	UNLOCK(&lock);
2053170222Sdougb#endif
2054170222Sdougb}
2055170222Sdougb
2056186462Sdougbvoid
2057170222Sdougbisc_mem_checkdestroyed(FILE *file) {
2058170222Sdougb
2059170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2060170222Sdougb
2061170222Sdougb	LOCK(&lock);
2062170222Sdougb	if (!ISC_LIST_EMPTY(contexts))  {
2063170222Sdougb#if ISC_MEM_TRACKLINES
2064170222Sdougb		isc_mem_t *ctx;
2065170222Sdougb
2066170222Sdougb		for (ctx = ISC_LIST_HEAD(contexts);
2067170222Sdougb		     ctx != NULL;
2068170222Sdougb		     ctx = ISC_LIST_NEXT(ctx, link)) {
2069170222Sdougb			fprintf(file, "context: %p\n", ctx);
2070170222Sdougb			print_active(ctx, file);
2071170222Sdougb		}
2072170222Sdougb		fflush(file);
2073170222Sdougb#endif
2074174187Sdougb		INSIST(0);
2075170222Sdougb	}
2076170222Sdougb	UNLOCK(&lock);
2077170222Sdougb}
2078193149Sdougb
2079193149Sdougbunsigned int
2080193149Sdougbisc_mem_references(isc_mem_t *ctx) {
2081193149Sdougb	unsigned int references;
2082193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2083193149Sdougb
2084193149Sdougb	MCTXLOCK(ctx, &ctx->lock);
2085193149Sdougb	references = ctx->references;
2086193149Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
2087193149Sdougb
2088193149Sdougb	return (references);
2089193149Sdougb}
2090193149Sdougb
2091193149Sdougb#ifdef HAVE_LIBXML2
2092193149Sdougb
2093193149Sdougbtypedef struct summarystat {
2094193149Sdougb	isc_uint64_t	total;
2095193149Sdougb	isc_uint64_t	inuse;
2096193149Sdougb	isc_uint64_t	blocksize;
2097193149Sdougb	isc_uint64_t	contextsize;
2098193149Sdougb} summarystat_t;
2099193149Sdougb
2100193149Sdougbstatic void
2101193149Sdougbrenderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
2102193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2103193149Sdougb
2104193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "context");
2105193149Sdougb
2106193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
2107193149Sdougb	xmlTextWriterWriteFormatString(writer, "%p", ctx);
2108193149Sdougb	xmlTextWriterEndElement(writer); /* id */
2109193149Sdougb
2110193149Sdougb	if (ctx->name[0] != 0) {
2111193149Sdougb		xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
2112193149Sdougb		xmlTextWriterWriteFormatString(writer, "%s", ctx->name);
2113193149Sdougb		xmlTextWriterEndElement(writer); /* name */
2114193149Sdougb	}
2115193149Sdougb
2116193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2117193149Sdougb	MCTXLOCK(ctx, &ctx->lock);
2118193149Sdougb
2119193149Sdougb	summary->contextsize += sizeof(*ctx) +
2120193149Sdougb		(ctx->max_size + 1) * sizeof(struct stats) +
2121193149Sdougb		ctx->max_size * sizeof(element *) +
2122193149Sdougb		ctx->basic_table_count * sizeof(char *);
2123193149Sdougb#if ISC_MEM_TRACKLINES
2124193149Sdougb	if (ctx->debuglist != NULL) {
2125193149Sdougb		summary->contextsize +=
2126193149Sdougb			(ctx->max_size + 1) * sizeof(debuglist_t) +
2127193149Sdougb			ctx->debuglistcnt * sizeof(debuglink_t);
2128193149Sdougb	}
2129193149Sdougb#endif
2130193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
2131193149Sdougb	xmlTextWriterWriteFormatString(writer, "%d", ctx->references);
2132193149Sdougb	xmlTextWriterEndElement(writer); /* references */
2133193149Sdougb
2134193149Sdougb	summary->total += ctx->total;
2135193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "total");
2136193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2137193149Sdougb				       (isc_uint64_t)ctx->total);
2138193149Sdougb	xmlTextWriterEndElement(writer); /* total */
2139193149Sdougb
2140193149Sdougb	summary->inuse += ctx->inuse;
2141193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse");
2142193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2143193149Sdougb				       (isc_uint64_t)ctx->inuse);
2144193149Sdougb	xmlTextWriterEndElement(writer); /* inuse */
2145193149Sdougb
2146193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse");
2147193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2148193149Sdougb				       (isc_uint64_t)ctx->maxinuse);
2149193149Sdougb	xmlTextWriterEndElement(writer); /* maxinuse */
2150193149Sdougb
2151193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize");
2152193149Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2153193149Sdougb		summary->blocksize += ctx->basic_table_count *
2154193149Sdougb			NUM_BASIC_BLOCKS * ctx->mem_target;
2155193149Sdougb		xmlTextWriterWriteFormatString(writer,
2156193149Sdougb					       "%" ISC_PRINT_QUADFORMAT "u",
2157193149Sdougb					       (isc_uint64_t)
2158193149Sdougb					       ctx->basic_table_count *
2159193149Sdougb					       NUM_BASIC_BLOCKS *
2160193149Sdougb					       ctx->mem_target);
2161193149Sdougb	} else
2162193149Sdougb		xmlTextWriterWriteFormatString(writer, "%s", "-");
2163193149Sdougb	xmlTextWriterEndElement(writer); /* blocksize */
2164193149Sdougb
2165193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools");
2166193149Sdougb	xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt);
2167193149Sdougb	xmlTextWriterEndElement(writer); /* pools */
2168193149Sdougb	summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2169193149Sdougb
2170193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater");
2171193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2172193149Sdougb				       (isc_uint64_t)ctx->hi_water);
2173193149Sdougb	xmlTextWriterEndElement(writer); /* hiwater */
2174193149Sdougb
2175193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater");
2176193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2177193149Sdougb				       (isc_uint64_t)ctx->lo_water);
2178193149Sdougb	xmlTextWriterEndElement(writer); /* lowater */
2179193149Sdougb
2180193149Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
2181193149Sdougb
2182193149Sdougb	xmlTextWriterEndElement(writer); /* context */
2183193149Sdougb}
2184193149Sdougb
2185193149Sdougbvoid
2186193149Sdougbisc_mem_renderxml(xmlTextWriterPtr writer) {
2187193149Sdougb	isc_mem_t *ctx;
2188193149Sdougb	summarystat_t summary;
2189193149Sdougb	isc_uint64_t lost;
2190193149Sdougb
2191193149Sdougb	memset(&summary, 0, sizeof(summary));
2192193149Sdougb
2193193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts");
2194193149Sdougb
2195193149Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2196193149Sdougb
2197193149Sdougb	LOCK(&lock);
2198193149Sdougb	lost = totallost;
2199193149Sdougb	for (ctx = ISC_LIST_HEAD(contexts);
2200193149Sdougb	     ctx != NULL;
2201193149Sdougb	     ctx = ISC_LIST_NEXT(ctx, link)) {
2202193149Sdougb		renderctx(ctx, &summary, writer);
2203193149Sdougb	}
2204193149Sdougb	UNLOCK(&lock);
2205193149Sdougb
2206193149Sdougb	xmlTextWriterEndElement(writer); /* contexts */
2207193149Sdougb
2208193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary");
2209193149Sdougb
2210193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse");
2211193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2212193149Sdougb				       summary.total);
2213193149Sdougb	xmlTextWriterEndElement(writer); /* TotalUse */
2214193149Sdougb
2215193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse");
2216193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2217193149Sdougb				       summary.inuse);
2218193149Sdougb	xmlTextWriterEndElement(writer); /* InUse */
2219193149Sdougb
2220193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize");
2221193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2222193149Sdougb				       summary.blocksize);
2223193149Sdougb	xmlTextWriterEndElement(writer); /* BlockSize */
2224193149Sdougb
2225193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize");
2226193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2227193149Sdougb				       summary.contextsize);
2228193149Sdougb	xmlTextWriterEndElement(writer); /* ContextSize */
2229193149Sdougb
2230193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost");
2231193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2232193149Sdougb				       lost);
2233193149Sdougb	xmlTextWriterEndElement(writer); /* Lost */
2234193149Sdougb
2235193149Sdougb	xmlTextWriterEndElement(writer); /* summary */
2236193149Sdougb}
2237193149Sdougb
2238193149Sdougb#endif /* HAVE_LIBXML2 */
2239