mem.c revision 193149
1135446Strhodes/*
2193149Sdougb * Copyright (C) 2004-2009  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
18193149Sdougb/* $Id: mem.c,v 1.145.120.4 2009/02/16 03:17:05 marka 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
75135446Strhodes#define FLARG		, const char *file, 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;
144135446Strhodes	isc_mem_water_t		water;
145135446Strhodes	void *			water_arg;
146135446Strhodes	ISC_LIST(isc_mempool_t)	pools;
147193149Sdougb	unsigned int		poolcnt;
148135446Strhodes
149170222Sdougb	/*  ISC_MEMFLAG_INTERNAL */
150135446Strhodes	size_t			mem_target;
151135446Strhodes	element **		freelists;
152135446Strhodes	element *		basic_blocks;
153135446Strhodes	unsigned char **	basic_table;
154135446Strhodes	unsigned int		basic_table_count;
155135446Strhodes	unsigned int		basic_table_size;
156135446Strhodes	unsigned char *		lowest;
157135446Strhodes	unsigned char *		highest;
158135446Strhodes
159135446Strhodes#if ISC_MEM_TRACKLINES
160135446Strhodes	debuglist_t *	 	debuglist;
161193149Sdougb	unsigned int		debuglistcnt;
162135446Strhodes#endif
163135446Strhodes
164135446Strhodes	unsigned int		memalloc_failures;
165170222Sdougb	ISC_LINK(isc_mem_t)	link;
166135446Strhodes};
167135446Strhodes
168135446Strhodes#define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
169135446Strhodes#define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
170135446Strhodes
171135446Strhodesstruct isc_mempool {
172135446Strhodes	/* always unlocked */
173170222Sdougb	unsigned int	magic;		/*%< magic number */
174170222Sdougb	isc_mutex_t    *lock;		/*%< optional lock */
175170222Sdougb	isc_mem_t      *mctx;		/*%< our memory context */
176170222Sdougb	/*%< locked via the memory context's lock */
177170222Sdougb	ISC_LINK(isc_mempool_t)	link;	/*%< next pool in this mem context */
178170222Sdougb	/*%< optionally locked from here down */
179170222Sdougb	element	       *items;		/*%< low water item list */
180170222Sdougb	size_t		size;		/*%< size of each item on this pool */
181170222Sdougb	unsigned int	maxalloc;	/*%< max number of items allowed */
182170222Sdougb	unsigned int	allocated;	/*%< # of items currently given out */
183170222Sdougb	unsigned int	freecount;	/*%< # of items on reserved list */
184170222Sdougb	unsigned int	freemax;	/*%< # of items allowed on free list */
185170222Sdougb	unsigned int	fillcount;	/*%< # of items to fetch on each fill */
186170222Sdougb	/*%< Stats only. */
187170222Sdougb	unsigned int	gets;		/*%< # of requests to this pool */
188170222Sdougb	/*%< Debugging only. */
189135446Strhodes#if ISC_MEMPOOL_NAMES
190170222Sdougb	char		name[16];	/*%< printed name in stats reports */
191135446Strhodes#endif
192135446Strhodes};
193135446Strhodes
194135446Strhodes/*
195135446Strhodes * Private Inline-able.
196135446Strhodes */
197135446Strhodes
198135446Strhodes#if ! ISC_MEM_TRACKLINES
199135446Strhodes#define ADD_TRACE(a, b, c, d, e)
200135446Strhodes#define DELETE_TRACE(a, b, c, d, e)
201135446Strhodes#else
202135446Strhodes#define ADD_TRACE(a, b, c, d, e) \
203135446Strhodes	do { \
204135446Strhodes		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
205135446Strhodes					  ISC_MEM_DEBUGRECORD)) != 0 && \
206135446Strhodes		     b != NULL) \
207186462Sdougb			 add_trace_entry(a, b, c, d, e); \
208135446Strhodes	} while (0)
209135446Strhodes#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)
210135446Strhodes
211135446Strhodesstatic void
212135446Strhodesprint_active(isc_mem_t *ctx, FILE *out);
213135446Strhodes
214170222Sdougb/*!
215135446Strhodes * mctx must be locked.
216135446Strhodes */
217135446Strhodesstatic inline void
218135446Strhodesadd_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size
219135446Strhodes		FLARG)
220135446Strhodes{
221135446Strhodes	debuglink_t *dl;
222135446Strhodes	unsigned int i;
223135446Strhodes
224135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
225135446Strhodes		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
226135446Strhodes					       ISC_MSG_ADDTRACE,
227135446Strhodes					       "add %p size %u "
228135446Strhodes					       "file %s line %u mctx %p\n"),
229135446Strhodes			ptr, size, file, line, mctx);
230135446Strhodes
231135446Strhodes	if (mctx->debuglist == NULL)
232135446Strhodes		return;
233135446Strhodes
234135446Strhodes	if (size > mctx->max_size)
235135446Strhodes		size = mctx->max_size;
236135446Strhodes
237135446Strhodes	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
238135446Strhodes	while (dl != NULL) {
239135446Strhodes		if (dl->count == DEBUGLIST_COUNT)
240135446Strhodes			goto next;
241135446Strhodes		for (i = 0; i < DEBUGLIST_COUNT; i++) {
242135446Strhodes			if (dl->ptr[i] == NULL) {
243135446Strhodes				dl->ptr[i] = ptr;
244135446Strhodes				dl->size[i] = size;
245135446Strhodes				dl->file[i] = file;
246135446Strhodes				dl->line[i] = line;
247135446Strhodes				dl->count++;
248135446Strhodes				return;
249135446Strhodes			}
250135446Strhodes		}
251135446Strhodes	next:
252135446Strhodes		dl = ISC_LIST_NEXT(dl, link);
253135446Strhodes	}
254135446Strhodes
255135446Strhodes	dl = malloc(sizeof(debuglink_t));
256135446Strhodes	INSIST(dl != NULL);
257135446Strhodes
258135446Strhodes	ISC_LINK_INIT(dl, link);
259135446Strhodes	for (i = 1; i < DEBUGLIST_COUNT; i++) {
260135446Strhodes		dl->ptr[i] = NULL;
261135446Strhodes		dl->size[i] = 0;
262135446Strhodes		dl->file[i] = NULL;
263135446Strhodes		dl->line[i] = 0;
264135446Strhodes	}
265135446Strhodes
266135446Strhodes	dl->ptr[0] = ptr;
267135446Strhodes	dl->size[0] = size;
268135446Strhodes	dl->file[0] = file;
269135446Strhodes	dl->line[0] = line;
270135446Strhodes	dl->count = 1;
271135446Strhodes
272135446Strhodes	ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);
273193149Sdougb	mctx->debuglistcnt++;
274135446Strhodes}
275135446Strhodes
276135446Strhodesstatic inline void
277135446Strhodesdelete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size,
278135446Strhodes		   const char *file, unsigned int line)
279135446Strhodes{
280135446Strhodes	debuglink_t *dl;
281135446Strhodes	unsigned int i;
282135446Strhodes
283135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
284135446Strhodes		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
285135446Strhodes					       ISC_MSG_DELTRACE,
286135446Strhodes					       "del %p size %u "
287135446Strhodes					       "file %s line %u mctx %p\n"),
288135446Strhodes			ptr, size, file, line, mctx);
289135446Strhodes
290135446Strhodes	if (mctx->debuglist == NULL)
291135446Strhodes		return;
292135446Strhodes
293135446Strhodes	if (size > mctx->max_size)
294135446Strhodes		size = mctx->max_size;
295135446Strhodes
296135446Strhodes	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
297135446Strhodes	while (dl != NULL) {
298135446Strhodes		for (i = 0; i < DEBUGLIST_COUNT; i++) {
299135446Strhodes			if (dl->ptr[i] == ptr) {
300135446Strhodes				dl->ptr[i] = NULL;
301135446Strhodes				dl->size[i] = 0;
302135446Strhodes				dl->file[i] = NULL;
303135446Strhodes				dl->line[i] = 0;
304135446Strhodes
305135446Strhodes				INSIST(dl->count > 0);
306135446Strhodes				dl->count--;
307135446Strhodes				if (dl->count == 0) {
308135446Strhodes					ISC_LIST_UNLINK(mctx->debuglist[size],
309135446Strhodes							dl, link);
310135446Strhodes					free(dl);
311135446Strhodes				}
312135446Strhodes				return;
313135446Strhodes			}
314135446Strhodes		}
315135446Strhodes		dl = ISC_LIST_NEXT(dl, link);
316135446Strhodes	}
317135446Strhodes
318135446Strhodes	/*
319135446Strhodes	 * If we get here, we didn't find the item on the list.  We're
320135446Strhodes	 * screwed.
321135446Strhodes	 */
322135446Strhodes	INSIST(dl != NULL);
323135446Strhodes}
324135446Strhodes#endif /* ISC_MEM_TRACKLINES */
325135446Strhodes
326135446Strhodesstatic inline size_t
327135446Strhodesrmsize(size_t size) {
328135446Strhodes	/*
329186462Sdougb	 * round down to ALIGNMENT_SIZE
330135446Strhodes	 */
331135446Strhodes	return (size & (~(ALIGNMENT_SIZE - 1)));
332135446Strhodes}
333135446Strhodes
334135446Strhodesstatic inline size_t
335135446Strhodesquantize(size_t size) {
336170222Sdougb	/*!
337135446Strhodes	 * Round up the result in order to get a size big
338135446Strhodes	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
339135446Strhodes	 * byte boundaries.
340135446Strhodes	 */
341135446Strhodes
342170222Sdougb	if (size == 0U)
343135446Strhodes		return (ALIGNMENT_SIZE);
344135446Strhodes	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
345135446Strhodes}
346135446Strhodes
347135446Strhodesstatic inline isc_boolean_t
348135446Strhodesmore_basic_blocks(isc_mem_t *ctx) {
349135446Strhodes	void *new;
350135446Strhodes	unsigned char *curr, *next;
351135446Strhodes	unsigned char *first, *last;
352135446Strhodes	unsigned char **table;
353135446Strhodes	unsigned int table_size;
354135446Strhodes	size_t increment;
355135446Strhodes	int i;
356135446Strhodes
357135446Strhodes	/* Require: we hold the context lock. */
358135446Strhodes
359135446Strhodes	/*
360135446Strhodes	 * Did we hit the quota for this context?
361135446Strhodes	 */
362135446Strhodes	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
363170222Sdougb	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
364135446Strhodes		return (ISC_FALSE);
365135446Strhodes
366135446Strhodes	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
367135446Strhodes	if (ctx->basic_table_count == ctx->basic_table_size) {
368135446Strhodes		table_size = ctx->basic_table_size + TABLE_INCREMENT;
369135446Strhodes		table = (ctx->memalloc)(ctx->arg,
370135446Strhodes					table_size * sizeof(unsigned char *));
371135446Strhodes		if (table == NULL) {
372135446Strhodes			ctx->memalloc_failures++;
373135446Strhodes			return (ISC_FALSE);
374135446Strhodes		}
375135446Strhodes		if (ctx->basic_table_size != 0) {
376135446Strhodes			memcpy(table, ctx->basic_table,
377135446Strhodes			       ctx->basic_table_size *
378135446Strhodes			       sizeof(unsigned char *));
379135446Strhodes			(ctx->memfree)(ctx->arg, ctx->basic_table);
380135446Strhodes		}
381135446Strhodes		ctx->basic_table = table;
382135446Strhodes		ctx->basic_table_size = table_size;
383135446Strhodes	}
384135446Strhodes
385135446Strhodes	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
386135446Strhodes	if (new == NULL) {
387135446Strhodes		ctx->memalloc_failures++;
388135446Strhodes		return (ISC_FALSE);
389135446Strhodes	}
390135446Strhodes	ctx->total += increment;
391135446Strhodes	ctx->basic_table[ctx->basic_table_count] = new;
392135446Strhodes	ctx->basic_table_count++;
393135446Strhodes
394135446Strhodes	curr = new;
395135446Strhodes	next = curr + ctx->mem_target;
396135446Strhodes	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
397135446Strhodes		((element *)curr)->next = (element *)next;
398135446Strhodes		curr = next;
399135446Strhodes		next += ctx->mem_target;
400135446Strhodes	}
401135446Strhodes	/*
402135446Strhodes	 * curr is now pointing at the last block in the
403135446Strhodes	 * array.
404135446Strhodes	 */
405135446Strhodes	((element *)curr)->next = NULL;
406135446Strhodes	first = new;
407135446Strhodes	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
408135446Strhodes	if (first < ctx->lowest || ctx->lowest == NULL)
409135446Strhodes		ctx->lowest = first;
410135446Strhodes	if (last > ctx->highest)
411135446Strhodes		ctx->highest = last;
412135446Strhodes	ctx->basic_blocks = new;
413135446Strhodes
414135446Strhodes	return (ISC_TRUE);
415135446Strhodes}
416135446Strhodes
417135446Strhodesstatic inline isc_boolean_t
418135446Strhodesmore_frags(isc_mem_t *ctx, size_t new_size) {
419135446Strhodes	int i, frags;
420135446Strhodes	size_t total_size;
421135446Strhodes	void *new;
422135446Strhodes	unsigned char *curr, *next;
423135446Strhodes
424170222Sdougb	/*!
425135446Strhodes	 * Try to get more fragments by chopping up a basic block.
426135446Strhodes	 */
427135446Strhodes
428135446Strhodes	if (ctx->basic_blocks == NULL) {
429135446Strhodes		if (!more_basic_blocks(ctx)) {
430135446Strhodes			/*
431135446Strhodes			 * We can't get more memory from the OS, or we've
432135446Strhodes			 * hit the quota for this context.
433135446Strhodes			 */
434135446Strhodes			/*
435135446Strhodes			 * XXXRTH  "At quota" notification here.
436135446Strhodes			 */
437135446Strhodes			return (ISC_FALSE);
438135446Strhodes		}
439135446Strhodes	}
440135446Strhodes
441135446Strhodes	total_size = ctx->mem_target;
442135446Strhodes	new = ctx->basic_blocks;
443135446Strhodes	ctx->basic_blocks = ctx->basic_blocks->next;
444135446Strhodes	frags = total_size / new_size;
445135446Strhodes	ctx->stats[new_size].blocks++;
446135446Strhodes	ctx->stats[new_size].freefrags += frags;
447135446Strhodes	/*
448135446Strhodes	 * Set up a linked-list of blocks of size
449135446Strhodes	 * "new_size".
450135446Strhodes	 */
451135446Strhodes	curr = new;
452135446Strhodes	next = curr + new_size;
453135446Strhodes	total_size -= new_size;
454135446Strhodes	for (i = 0; i < (frags - 1); i++) {
455135446Strhodes		((element *)curr)->next = (element *)next;
456135446Strhodes		curr = next;
457135446Strhodes		next += new_size;
458135446Strhodes		total_size -= new_size;
459135446Strhodes	}
460135446Strhodes	/*
461135446Strhodes	 * Add the remaining fragment of the basic block to a free list.
462135446Strhodes	 */
463135446Strhodes	total_size = rmsize(total_size);
464170222Sdougb	if (total_size > 0U) {
465135446Strhodes		((element *)next)->next = ctx->freelists[total_size];
466135446Strhodes		ctx->freelists[total_size] = (element *)next;
467135446Strhodes		ctx->stats[total_size].freefrags++;
468135446Strhodes	}
469135446Strhodes	/*
470135446Strhodes	 * curr is now pointing at the last block in the
471135446Strhodes	 * array.
472135446Strhodes	 */
473135446Strhodes	((element *)curr)->next = NULL;
474135446Strhodes	ctx->freelists[new_size] = new;
475135446Strhodes
476135446Strhodes	return (ISC_TRUE);
477135446Strhodes}
478135446Strhodes
479135446Strhodesstatic inline void *
480135446Strhodesmem_getunlocked(isc_mem_t *ctx, size_t size) {
481135446Strhodes	size_t new_size = quantize(size);
482135446Strhodes	void *ret;
483135446Strhodes
484135446Strhodes	if (size >= ctx->max_size || new_size >= ctx->max_size) {
485135446Strhodes		/*
486135446Strhodes		 * memget() was called on something beyond our upper limit.
487135446Strhodes		 */
488170222Sdougb		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
489135446Strhodes			ret = NULL;
490135446Strhodes			goto done;
491135446Strhodes		}
492135446Strhodes		ret = (ctx->memalloc)(ctx->arg, size);
493135446Strhodes		if (ret == NULL) {
494135446Strhodes			ctx->memalloc_failures++;
495135446Strhodes			goto done;
496135446Strhodes		}
497135446Strhodes		ctx->total += size;
498135446Strhodes		ctx->inuse += size;
499135446Strhodes		ctx->stats[ctx->max_size].gets++;
500135446Strhodes		ctx->stats[ctx->max_size].totalgets++;
501135446Strhodes		/*
502135446Strhodes		 * If we don't set new_size to size, then the
503135446Strhodes		 * ISC_MEM_FILL code might write over bytes we
504135446Strhodes		 * don't own.
505135446Strhodes		 */
506135446Strhodes		new_size = size;
507135446Strhodes		goto done;
508135446Strhodes	}
509135446Strhodes
510135446Strhodes	/*
511135446Strhodes	 * If there are no blocks in the free list for this size, get a chunk
512135446Strhodes	 * of memory and then break it up into "new_size"-sized blocks, adding
513135446Strhodes	 * them to the free list.
514135446Strhodes	 */
515135446Strhodes	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
516135446Strhodes		return (NULL);
517135446Strhodes
518135446Strhodes	/*
519135446Strhodes	 * The free list uses the "rounded-up" size "new_size".
520135446Strhodes	 */
521135446Strhodes	ret = ctx->freelists[new_size];
522135446Strhodes	ctx->freelists[new_size] = ctx->freelists[new_size]->next;
523135446Strhodes
524135446Strhodes	/*
525135446Strhodes	 * The stats[] uses the _actual_ "size" requested by the
526135446Strhodes	 * caller, with the caveat (in the code above) that "size" >= the
527135446Strhodes	 * max. size (max_size) ends up getting recorded as a call to
528135446Strhodes	 * max_size.
529135446Strhodes	 */
530135446Strhodes	ctx->stats[size].gets++;
531135446Strhodes	ctx->stats[size].totalgets++;
532135446Strhodes	ctx->stats[new_size].freefrags--;
533135446Strhodes	ctx->inuse += new_size;
534135446Strhodes
535135446Strhodes done:
536135446Strhodes
537135446Strhodes#if ISC_MEM_FILL
538135446Strhodes	if (ret != NULL)
539135446Strhodes		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
540135446Strhodes#endif
541135446Strhodes
542135446Strhodes	return (ret);
543135446Strhodes}
544135446Strhodes
545135446Strhodes#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
546135446Strhodesstatic inline void
547135446Strhodescheck_overrun(void *mem, size_t size, size_t new_size) {
548135446Strhodes	unsigned char *cp;
549135446Strhodes
550135446Strhodes	cp = (unsigned char *)mem;
551135446Strhodes	cp += size;
552135446Strhodes	while (size < new_size) {
553135446Strhodes		INSIST(*cp == 0xbe);
554135446Strhodes		cp++;
555135446Strhodes		size++;
556135446Strhodes	}
557135446Strhodes}
558135446Strhodes#endif
559135446Strhodes
560135446Strhodesstatic inline void
561135446Strhodesmem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
562135446Strhodes	size_t new_size = quantize(size);
563135446Strhodes
564135446Strhodes	if (size == ctx->max_size || new_size >= ctx->max_size) {
565135446Strhodes		/*
566135446Strhodes		 * memput() called on something beyond our upper limit.
567135446Strhodes		 */
568135446Strhodes#if ISC_MEM_FILL
569135446Strhodes		memset(mem, 0xde, size); /* Mnemonic for "dead". */
570135446Strhodes#endif
571135446Strhodes		(ctx->memfree)(ctx->arg, mem);
572170222Sdougb		INSIST(ctx->stats[ctx->max_size].gets != 0U);
573135446Strhodes		ctx->stats[ctx->max_size].gets--;
574135446Strhodes		INSIST(size <= ctx->total);
575135446Strhodes		ctx->inuse -= size;
576135446Strhodes		ctx->total -= size;
577135446Strhodes		return;
578135446Strhodes	}
579135446Strhodes
580135446Strhodes#if ISC_MEM_FILL
581135446Strhodes#if ISC_MEM_CHECKOVERRUN
582135446Strhodes	check_overrun(mem, size, new_size);
583135446Strhodes#endif
584135446Strhodes	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
585135446Strhodes#endif
586135446Strhodes
587135446Strhodes	/*
588135446Strhodes	 * The free list uses the "rounded-up" size "new_size".
589135446Strhodes	 */
590135446Strhodes	((element *)mem)->next = ctx->freelists[new_size];
591135446Strhodes	ctx->freelists[new_size] = (element *)mem;
592135446Strhodes
593135446Strhodes	/*
594135446Strhodes	 * The stats[] uses the _actual_ "size" requested by the
595135446Strhodes	 * caller, with the caveat (in the code above) that "size" >= the
596135446Strhodes	 * max. size (max_size) ends up getting recorded as a call to
597135446Strhodes	 * max_size.
598135446Strhodes	 */
599170222Sdougb	INSIST(ctx->stats[size].gets != 0U);
600135446Strhodes	ctx->stats[size].gets--;
601135446Strhodes	ctx->stats[new_size].freefrags++;
602135446Strhodes	ctx->inuse -= new_size;
603135446Strhodes}
604135446Strhodes
605170222Sdougb/*!
606135446Strhodes * Perform a malloc, doing memory filling and overrun detection as necessary.
607135446Strhodes */
608135446Strhodesstatic inline void *
609135446Strhodesmem_get(isc_mem_t *ctx, size_t size) {
610135446Strhodes	char *ret;
611135446Strhodes
612135446Strhodes#if ISC_MEM_CHECKOVERRUN
613135446Strhodes	size += 1;
614135446Strhodes#endif
615135446Strhodes
616135446Strhodes	ret = (ctx->memalloc)(ctx->arg, size);
617135446Strhodes	if (ret == NULL)
618186462Sdougb		ctx->memalloc_failures++;
619135446Strhodes
620135446Strhodes#if ISC_MEM_FILL
621135446Strhodes	if (ret != NULL)
622135446Strhodes		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
623135446Strhodes#else
624135446Strhodes#  if ISC_MEM_CHECKOVERRUN
625135446Strhodes	if (ret != NULL)
626135446Strhodes		ret[size-1] = 0xbe;
627135446Strhodes#  endif
628135446Strhodes#endif
629135446Strhodes
630135446Strhodes	return (ret);
631135446Strhodes}
632135446Strhodes
633170222Sdougb/*!
634135446Strhodes * Perform a free, doing memory filling and overrun detection as necessary.
635135446Strhodes */
636135446Strhodesstatic inline void
637135446Strhodesmem_put(isc_mem_t *ctx, void *mem, size_t size) {
638135446Strhodes#if ISC_MEM_CHECKOVERRUN
639135446Strhodes	INSIST(((unsigned char *)mem)[size] == 0xbe);
640135446Strhodes#endif
641135446Strhodes#if ISC_MEM_FILL
642135446Strhodes	memset(mem, 0xde, size); /* Mnemonic for "dead". */
643135446Strhodes#else
644135446Strhodes	UNUSED(size);
645135446Strhodes#endif
646135446Strhodes	(ctx->memfree)(ctx->arg, mem);
647135446Strhodes}
648135446Strhodes
649170222Sdougb/*!
650135446Strhodes * Update internal counters after a memory get.
651135446Strhodes */
652135446Strhodesstatic inline void
653135446Strhodesmem_getstats(isc_mem_t *ctx, size_t size) {
654135446Strhodes	ctx->total += size;
655135446Strhodes	ctx->inuse += size;
656135446Strhodes
657135446Strhodes	if (size > ctx->max_size) {
658135446Strhodes		ctx->stats[ctx->max_size].gets++;
659135446Strhodes		ctx->stats[ctx->max_size].totalgets++;
660135446Strhodes	} else {
661135446Strhodes		ctx->stats[size].gets++;
662135446Strhodes		ctx->stats[size].totalgets++;
663135446Strhodes	}
664135446Strhodes}
665135446Strhodes
666170222Sdougb/*!
667135446Strhodes * Update internal counters after a memory put.
668135446Strhodes */
669135446Strhodesstatic inline void
670135446Strhodesmem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
671135446Strhodes	UNUSED(ptr);
672135446Strhodes
673135446Strhodes	INSIST(ctx->inuse >= size);
674135446Strhodes	ctx->inuse -= size;
675135446Strhodes
676135446Strhodes	if (size > ctx->max_size) {
677135446Strhodes		INSIST(ctx->stats[ctx->max_size].gets > 0U);
678135446Strhodes		ctx->stats[ctx->max_size].gets--;
679135446Strhodes	} else {
680135446Strhodes		INSIST(ctx->stats[size].gets > 0U);
681135446Strhodes		ctx->stats[size].gets--;
682135446Strhodes	}
683135446Strhodes}
684135446Strhodes
685135446Strhodes/*
686135446Strhodes * Private.
687135446Strhodes */
688135446Strhodes
689135446Strhodesstatic void *
690135446Strhodesdefault_memalloc(void *arg, size_t size) {
691135446Strhodes	UNUSED(arg);
692135446Strhodes	if (size == 0U)
693135446Strhodes		size = 1;
694135446Strhodes	return (malloc(size));
695135446Strhodes}
696135446Strhodes
697135446Strhodesstatic void
698135446Strhodesdefault_memfree(void *arg, void *ptr) {
699135446Strhodes	UNUSED(arg);
700135446Strhodes	free(ptr);
701135446Strhodes}
702135446Strhodes
703170222Sdougbstatic void
704170222Sdougbinitialize_action(void) {
705174187Sdougb	RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
706174187Sdougb	ISC_LIST_INIT(contexts);
707193149Sdougb	totallost = 0;
708170222Sdougb}
709170222Sdougb
710135446Strhodes/*
711135446Strhodes * Public.
712135446Strhodes */
713135446Strhodes
714135446Strhodesisc_result_t
715135446Strhodesisc_mem_createx(size_t init_max_size, size_t target_size,
716135446Strhodes		isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
717135446Strhodes		isc_mem_t **ctxp)
718135446Strhodes{
719170222Sdougb	return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree,
720170222Sdougb				 arg, ctxp, ISC_MEMFLAG_DEFAULT));
721186462Sdougb
722170222Sdougb}
723170222Sdougb
724170222Sdougbisc_result_t
725170222Sdougbisc_mem_createx2(size_t init_max_size, size_t target_size,
726170222Sdougb		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
727170222Sdougb		 isc_mem_t **ctxp, unsigned int flags)
728170222Sdougb{
729135446Strhodes	isc_mem_t *ctx;
730135446Strhodes	isc_result_t result;
731135446Strhodes
732135446Strhodes	REQUIRE(ctxp != NULL && *ctxp == NULL);
733135446Strhodes	REQUIRE(memalloc != NULL);
734135446Strhodes	REQUIRE(memfree != NULL);
735135446Strhodes
736135446Strhodes	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);
737135446Strhodes
738170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
739135446Strhodes
740135446Strhodes	ctx = (memalloc)(arg, sizeof(*ctx));
741135446Strhodes	if (ctx == NULL)
742135446Strhodes		return (ISC_R_NOMEMORY);
743135446Strhodes
744170222Sdougb	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
745170222Sdougb		result = isc_mutex_init(&ctx->lock);
746170222Sdougb		if (result != ISC_R_SUCCESS) {
747170222Sdougb			(memfree)(arg, ctx);
748170222Sdougb			return (result);
749170222Sdougb		}
750153816Sdougb	}
751153816Sdougb
752135446Strhodes	if (init_max_size == 0U)
753135446Strhodes		ctx->max_size = DEF_MAX_SIZE;
754135446Strhodes	else
755135446Strhodes		ctx->max_size = init_max_size;
756170222Sdougb	ctx->flags = flags;
757135446Strhodes	ctx->references = 1;
758193149Sdougb	memset(ctx->name, 0, sizeof(ctx->name));
759193149Sdougb	ctx->tag = NULL;
760135446Strhodes	ctx->quota = 0;
761135446Strhodes	ctx->total = 0;
762135446Strhodes	ctx->inuse = 0;
763135446Strhodes	ctx->maxinuse = 0;
764135446Strhodes	ctx->hi_water = 0;
765135446Strhodes	ctx->lo_water = 0;
766135446Strhodes	ctx->hi_called = ISC_FALSE;
767135446Strhodes	ctx->water = NULL;
768135446Strhodes	ctx->water_arg = NULL;
769135446Strhodes	ctx->magic = MEM_MAGIC;
770135446Strhodes	isc_ondestroy_init(&ctx->ondestroy);
771135446Strhodes	ctx->memalloc = memalloc;
772135446Strhodes	ctx->memfree = memfree;
773135446Strhodes	ctx->arg = arg;
774135446Strhodes	ctx->stats = NULL;
775135446Strhodes	ctx->checkfree = ISC_TRUE;
776135446Strhodes#if ISC_MEM_TRACKLINES
777135446Strhodes	ctx->debuglist = NULL;
778193149Sdougb	ctx->debuglistcnt = 0;
779135446Strhodes#endif
780135446Strhodes	ISC_LIST_INIT(ctx->pools);
781193149Sdougb	ctx->poolcnt = 0;
782135446Strhodes	ctx->freelists = NULL;
783170222Sdougb	ctx->basic_blocks = NULL;
784170222Sdougb	ctx->basic_table = NULL;
785170222Sdougb	ctx->basic_table_count = 0;
786170222Sdougb	ctx->basic_table_size = 0;
787170222Sdougb	ctx->lowest = NULL;
788170222Sdougb	ctx->highest = NULL;
789135446Strhodes
790135446Strhodes	ctx->stats = (memalloc)(arg,
791135446Strhodes				(ctx->max_size+1) * sizeof(struct stats));
792135446Strhodes	if (ctx->stats == NULL) {
793135446Strhodes		result = ISC_R_NOMEMORY;
794135446Strhodes		goto error;
795135446Strhodes	}
796135446Strhodes	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
797135446Strhodes
798170222Sdougb	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
799170222Sdougb		if (target_size == 0U)
800170222Sdougb			ctx->mem_target = DEF_MEM_TARGET;
801170222Sdougb		else
802170222Sdougb			ctx->mem_target = target_size;
803170222Sdougb		ctx->freelists = (memalloc)(arg, ctx->max_size *
804170222Sdougb						 sizeof(element *));
805170222Sdougb		if (ctx->freelists == NULL) {
806170222Sdougb			result = ISC_R_NOMEMORY;
807170222Sdougb			goto error;
808170222Sdougb		}
809170222Sdougb		memset(ctx->freelists, 0,
810170222Sdougb		       ctx->max_size * sizeof(element *));
811135446Strhodes	}
812135446Strhodes
813135446Strhodes#if ISC_MEM_TRACKLINES
814135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
815135446Strhodes		unsigned int i;
816135446Strhodes
817135446Strhodes		ctx->debuglist = (memalloc)(arg,
818135446Strhodes				      (ctx->max_size+1) * sizeof(debuglist_t));
819135446Strhodes		if (ctx->debuglist == NULL) {
820135446Strhodes			result = ISC_R_NOMEMORY;
821135446Strhodes			goto error;
822135446Strhodes		}
823135446Strhodes		for (i = 0; i <= ctx->max_size; i++)
824135446Strhodes			ISC_LIST_INIT(ctx->debuglist[i]);
825135446Strhodes	}
826135446Strhodes#endif
827135446Strhodes
828135446Strhodes	ctx->memalloc_failures = 0;
829135446Strhodes
830170222Sdougb	LOCK(&lock);
831170222Sdougb	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
832170222Sdougb	UNLOCK(&lock);
833170222Sdougb
834135446Strhodes	*ctxp = ctx;
835135446Strhodes	return (ISC_R_SUCCESS);
836135446Strhodes
837135446Strhodes  error:
838153816Sdougb	if (ctx != NULL) {
839153816Sdougb		if (ctx->stats != NULL)
840135446Strhodes			(memfree)(arg, ctx->stats);
841153816Sdougb		if (ctx->freelists != NULL)
842135446Strhodes			(memfree)(arg, ctx->freelists);
843135446Strhodes#if ISC_MEM_TRACKLINES
844153816Sdougb		if (ctx->debuglist != NULL)
845135446Strhodes			(ctx->memfree)(ctx->arg, ctx->debuglist);
846135446Strhodes#endif /* ISC_MEM_TRACKLINES */
847170222Sdougb		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
848170222Sdougb			DESTROYLOCK(&ctx->lock);
849135446Strhodes		(memfree)(arg, ctx);
850135446Strhodes	}
851135446Strhodes
852135446Strhodes	return (result);
853135446Strhodes}
854135446Strhodes
855135446Strhodesisc_result_t
856135446Strhodesisc_mem_create(size_t init_max_size, size_t target_size,
857135446Strhodes	       isc_mem_t **ctxp)
858135446Strhodes{
859170222Sdougb	return (isc_mem_createx2(init_max_size, target_size,
860170222Sdougb				 default_memalloc, default_memfree, NULL,
861170222Sdougb				 ctxp, ISC_MEMFLAG_DEFAULT));
862135446Strhodes}
863135446Strhodes
864170222Sdougbisc_result_t
865170222Sdougbisc_mem_create2(size_t init_max_size, size_t target_size,
866170222Sdougb		isc_mem_t **ctxp, unsigned int flags)
867170222Sdougb{
868170222Sdougb	return (isc_mem_createx2(init_max_size, target_size,
869170222Sdougb				 default_memalloc, default_memfree, NULL,
870170222Sdougb				 ctxp, flags));
871170222Sdougb}
872170222Sdougb
873135446Strhodesstatic void
874135446Strhodesdestroy(isc_mem_t *ctx) {
875135446Strhodes	unsigned int i;
876135446Strhodes	isc_ondestroy_t ondest;
877135446Strhodes
878135446Strhodes	ctx->magic = 0;
879135446Strhodes
880170222Sdougb	LOCK(&lock);
881170222Sdougb	ISC_LIST_UNLINK(contexts, ctx, link);
882193149Sdougb	totallost += ctx->inuse;
883170222Sdougb	UNLOCK(&lock);
884170222Sdougb
885135446Strhodes	INSIST(ISC_LIST_EMPTY(ctx->pools));
886135446Strhodes
887135446Strhodes#if ISC_MEM_TRACKLINES
888135446Strhodes	if (ctx->debuglist != NULL) {
889135446Strhodes		if (ctx->checkfree) {
890135446Strhodes			for (i = 0; i <= ctx->max_size; i++) {
891135446Strhodes				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
892135446Strhodes					print_active(ctx, stderr);
893135446Strhodes				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
894135446Strhodes			}
895135446Strhodes		} else {
896135446Strhodes			debuglink_t *dl;
897135446Strhodes
898135446Strhodes			for (i = 0; i <= ctx->max_size; i++)
899135446Strhodes				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
900135446Strhodes				     dl != NULL;
901135446Strhodes				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
902135446Strhodes					ISC_LIST_UNLINK(ctx->debuglist[i],
903186462Sdougb							dl, link);
904135446Strhodes					free(dl);
905135446Strhodes				}
906135446Strhodes		}
907135446Strhodes		(ctx->memfree)(ctx->arg, ctx->debuglist);
908135446Strhodes	}
909135446Strhodes#endif
910135446Strhodes	INSIST(ctx->references == 0);
911135446Strhodes
912135446Strhodes	if (ctx->checkfree) {
913135446Strhodes		for (i = 0; i <= ctx->max_size; i++) {
914135446Strhodes#if ISC_MEM_TRACKLINES
915135446Strhodes			if (ctx->stats[i].gets != 0U)
916135446Strhodes				print_active(ctx, stderr);
917135446Strhodes#endif
918135446Strhodes			INSIST(ctx->stats[i].gets == 0U);
919135446Strhodes		}
920135446Strhodes	}
921135446Strhodes
922135446Strhodes	(ctx->memfree)(ctx->arg, ctx->stats);
923135446Strhodes
924170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
925170222Sdougb		for (i = 0; i < ctx->basic_table_count; i++)
926170222Sdougb			(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
927170222Sdougb		(ctx->memfree)(ctx->arg, ctx->freelists);
928186462Sdougb		if (ctx->basic_table != NULL)
929186462Sdougb			(ctx->memfree)(ctx->arg, ctx->basic_table);
930170222Sdougb	}
931135446Strhodes
932135446Strhodes	ondest = ctx->ondestroy;
933135446Strhodes
934170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
935170222Sdougb		DESTROYLOCK(&ctx->lock);
936135446Strhodes	(ctx->memfree)(ctx->arg, ctx);
937135446Strhodes
938135446Strhodes	isc_ondestroy_notify(&ondest, ctx);
939135446Strhodes}
940135446Strhodes
941135446Strhodesvoid
942135446Strhodesisc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
943135446Strhodes	REQUIRE(VALID_CONTEXT(source));
944135446Strhodes	REQUIRE(targetp != NULL && *targetp == NULL);
945135446Strhodes
946170222Sdougb	MCTXLOCK(source, &source->lock);
947135446Strhodes	source->references++;
948170222Sdougb	MCTXUNLOCK(source, &source->lock);
949135446Strhodes
950135446Strhodes	*targetp = source;
951135446Strhodes}
952135446Strhodes
953135446Strhodesvoid
954135446Strhodesisc_mem_detach(isc_mem_t **ctxp) {
955135446Strhodes	isc_mem_t *ctx;
956135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
957135446Strhodes
958135446Strhodes	REQUIRE(ctxp != NULL);
959135446Strhodes	ctx = *ctxp;
960135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
961135446Strhodes
962170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
963135446Strhodes	INSIST(ctx->references > 0);
964135446Strhodes	ctx->references--;
965135446Strhodes	if (ctx->references == 0)
966135446Strhodes		want_destroy = ISC_TRUE;
967170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
968135446Strhodes
969135446Strhodes	if (want_destroy)
970135446Strhodes		destroy(ctx);
971135446Strhodes
972135446Strhodes	*ctxp = NULL;
973135446Strhodes}
974135446Strhodes
975135446Strhodes/*
976135446Strhodes * isc_mem_putanddetach() is the equivalent of:
977135446Strhodes *
978135446Strhodes * mctx = NULL;
979135446Strhodes * isc_mem_attach(ptr->mctx, &mctx);
980135446Strhodes * isc_mem_detach(&ptr->mctx);
981135446Strhodes * isc_mem_put(mctx, ptr, sizeof(*ptr);
982135446Strhodes * isc_mem_detach(&mctx);
983135446Strhodes */
984135446Strhodes
985135446Strhodesvoid
986135446Strhodesisc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
987135446Strhodes	isc_mem_t *ctx;
988135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
989170222Sdougb	size_info *si;
990170222Sdougb	size_t oldsize;
991135446Strhodes
992135446Strhodes	REQUIRE(ctxp != NULL);
993135446Strhodes	ctx = *ctxp;
994135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
995135446Strhodes	REQUIRE(ptr != NULL);
996135446Strhodes
997135446Strhodes	/*
998135446Strhodes	 * Must be before mem_putunlocked() as ctxp is usually within
999135446Strhodes	 * [ptr..ptr+size).
1000135446Strhodes	 */
1001135446Strhodes	*ctxp = NULL;
1002135446Strhodes
1003170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1004170222Sdougb		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1005170222Sdougb			si = &(((size_info *)ptr)[-1]);
1006170222Sdougb			oldsize = si->u.size - ALIGNMENT_SIZE;
1007170222Sdougb			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1008170222Sdougb				oldsize -= ALIGNMENT_SIZE;
1009170222Sdougb			INSIST(oldsize == size);
1010170222Sdougb		}
1011170222Sdougb		isc__mem_free(ctx, ptr FLARG_PASS);
1012135446Strhodes
1013170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1014170222Sdougb		ctx->references--;
1015170222Sdougb		if (ctx->references == 0)
1016170222Sdougb			want_destroy = ISC_TRUE;
1017170222Sdougb		MCTXUNLOCK(ctx, &ctx->lock);
1018170222Sdougb		if (want_destroy)
1019170222Sdougb			destroy(ctx);
1020170222Sdougb
1021170222Sdougb		return;
1022170222Sdougb	}
1023170222Sdougb
1024170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1025170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1026170222Sdougb		mem_putunlocked(ctx, ptr, size);
1027170222Sdougb	} else {
1028170222Sdougb		mem_put(ctx, ptr, size);
1029170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1030170222Sdougb		mem_putstats(ctx, ptr, size);
1031170222Sdougb	}
1032170222Sdougb
1033135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1034135446Strhodes	INSIST(ctx->references > 0);
1035135446Strhodes	ctx->references--;
1036135446Strhodes	if (ctx->references == 0)
1037135446Strhodes		want_destroy = ISC_TRUE;
1038135446Strhodes
1039170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1040135446Strhodes
1041135446Strhodes	if (want_destroy)
1042135446Strhodes		destroy(ctx);
1043135446Strhodes}
1044135446Strhodes
1045135446Strhodesvoid
1046135446Strhodesisc_mem_destroy(isc_mem_t **ctxp) {
1047135446Strhodes	isc_mem_t *ctx;
1048135446Strhodes
1049135446Strhodes	/*
1050135446Strhodes	 * This routine provides legacy support for callers who use mctxs
1051135446Strhodes	 * without attaching/detaching.
1052135446Strhodes	 */
1053135446Strhodes
1054135446Strhodes	REQUIRE(ctxp != NULL);
1055135446Strhodes	ctx = *ctxp;
1056135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1057135446Strhodes
1058170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1059135446Strhodes#if ISC_MEM_TRACKLINES
1060135446Strhodes	if (ctx->references != 1)
1061135446Strhodes		print_active(ctx, stderr);
1062135446Strhodes#endif
1063135446Strhodes	REQUIRE(ctx->references == 1);
1064135446Strhodes	ctx->references--;
1065170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1066135446Strhodes
1067135446Strhodes	destroy(ctx);
1068135446Strhodes
1069135446Strhodes	*ctxp = NULL;
1070135446Strhodes}
1071135446Strhodes
1072135446Strhodesisc_result_t
1073135446Strhodesisc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) {
1074135446Strhodes	isc_result_t res;
1075135446Strhodes
1076170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1077135446Strhodes	res = isc_ondestroy_register(&ctx->ondestroy, task, event);
1078170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1079135446Strhodes
1080135446Strhodes	return (res);
1081135446Strhodes}
1082135446Strhodes
1083135446Strhodes
1084135446Strhodesvoid *
1085135446Strhodesisc__mem_get(isc_mem_t *ctx, size_t size FLARG) {
1086135446Strhodes	void *ptr;
1087135446Strhodes	isc_boolean_t call_water = ISC_FALSE;
1088135446Strhodes
1089135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1090135446Strhodes
1091170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
1092170222Sdougb		return (isc__mem_allocate(ctx, size FLARG_PASS));
1093135446Strhodes
1094170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1095170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1096170222Sdougb		ptr = mem_getunlocked(ctx, size);
1097170222Sdougb	} else {
1098170222Sdougb		ptr = mem_get(ctx, size);
1099170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1100170222Sdougb		if (ptr != NULL)
1101170222Sdougb			mem_getstats(ctx, size);
1102170222Sdougb	}
1103170222Sdougb
1104135446Strhodes	ADD_TRACE(ctx, ptr, size, file, line);
1105135446Strhodes	if (ctx->hi_water != 0U && !ctx->hi_called &&
1106135446Strhodes	    ctx->inuse > ctx->hi_water) {
1107135446Strhodes		call_water = ISC_TRUE;
1108135446Strhodes	}
1109135446Strhodes	if (ctx->inuse > ctx->maxinuse) {
1110135446Strhodes		ctx->maxinuse = ctx->inuse;
1111135446Strhodes		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1112135446Strhodes		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1113135446Strhodes			fprintf(stderr, "maxinuse = %lu\n",
1114135446Strhodes				(unsigned long)ctx->inuse);
1115135446Strhodes	}
1116170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1117135446Strhodes
1118135446Strhodes	if (call_water)
1119135446Strhodes		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1120135446Strhodes
1121135446Strhodes	return (ptr);
1122135446Strhodes}
1123135446Strhodes
1124135446Strhodesvoid
1125135446Strhodesisc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG)
1126135446Strhodes{
1127135446Strhodes	isc_boolean_t call_water = ISC_FALSE;
1128170222Sdougb	size_info *si;
1129170222Sdougb	size_t oldsize;
1130135446Strhodes
1131135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1132135446Strhodes	REQUIRE(ptr != NULL);
1133135446Strhodes
1134170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1135170222Sdougb		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1136170222Sdougb			si = &(((size_info *)ptr)[-1]);
1137170222Sdougb			oldsize = si->u.size - ALIGNMENT_SIZE;
1138170222Sdougb			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1139170222Sdougb				oldsize -= ALIGNMENT_SIZE;
1140170222Sdougb			INSIST(oldsize == size);
1141170222Sdougb		}
1142170222Sdougb		isc__mem_free(ctx, ptr FLARG_PASS);
1143170222Sdougb		return;
1144170222Sdougb	}
1145135446Strhodes
1146170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1147170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1148170222Sdougb		mem_putunlocked(ctx, ptr, size);
1149170222Sdougb	} else {
1150170222Sdougb		mem_put(ctx, ptr, size);
1151170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1152170222Sdougb		mem_putstats(ctx, ptr, size);
1153170222Sdougb	}
1154170222Sdougb
1155135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1156135446Strhodes
1157135446Strhodes	/*
1158135446Strhodes	 * The check against ctx->lo_water == 0 is for the condition
1159135446Strhodes	 * when the context was pushed over hi_water but then had
1160135446Strhodes	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1161135446Strhodes	 */
1162186462Sdougb	if (ctx->hi_called &&
1163135446Strhodes	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1164135446Strhodes		if (ctx->water != NULL)
1165135446Strhodes			call_water = ISC_TRUE;
1166135446Strhodes	}
1167170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1168135446Strhodes
1169135446Strhodes	if (call_water)
1170135446Strhodes		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1171135446Strhodes}
1172135446Strhodes
1173186462Sdougbvoid
1174186462Sdougbisc_mem_waterack(isc_mem_t *ctx, int flag) {
1175186462Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1176186462Sdougb
1177186462Sdougb	MCTXLOCK(ctx, &ctx->lock);
1178186462Sdougb	if (flag == ISC_MEM_LOWATER)
1179186462Sdougb		ctx->hi_called = ISC_FALSE;
1180186462Sdougb	else if (flag == ISC_MEM_HIWATER)
1181186462Sdougb		ctx->hi_called = ISC_TRUE;
1182186462Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1183186462Sdougb}
1184186462Sdougb
1185135446Strhodes#if ISC_MEM_TRACKLINES
1186135446Strhodesstatic void
1187135446Strhodesprint_active(isc_mem_t *mctx, FILE *out) {
1188135446Strhodes	if (mctx->debuglist != NULL) {
1189135446Strhodes		debuglink_t *dl;
1190135446Strhodes		unsigned int i, j;
1191135446Strhodes		const char *format;
1192135446Strhodes		isc_boolean_t found;
1193135446Strhodes
1194193149Sdougb		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1195135446Strhodes					    ISC_MSG_DUMPALLOC,
1196135446Strhodes					    "Dump of all outstanding "
1197135446Strhodes					    "memory allocations:\n"));
1198135446Strhodes		found = ISC_FALSE;
1199135446Strhodes		format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1200186462Sdougb					ISC_MSG_PTRFILELINE,
1201135446Strhodes					"\tptr %p size %u file %s line %u\n");
1202135446Strhodes		for (i = 0; i <= mctx->max_size; i++) {
1203135446Strhodes			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
1204186462Sdougb
1205135446Strhodes			if (dl != NULL)
1206135446Strhodes				found = ISC_TRUE;
1207135446Strhodes
1208135446Strhodes			while (dl != NULL) {
1209135446Strhodes				for (j = 0; j < DEBUGLIST_COUNT; j++)
1210135446Strhodes					if (dl->ptr[j] != NULL)
1211135446Strhodes						fprintf(out, format,
1212135446Strhodes							dl->ptr[j],
1213135446Strhodes							dl->size[j],
1214135446Strhodes							dl->file[j],
1215135446Strhodes							dl->line[j]);
1216135446Strhodes				dl = ISC_LIST_NEXT(dl, link);
1217135446Strhodes			}
1218135446Strhodes		}
1219135446Strhodes		if (!found)
1220193149Sdougb			fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1221135446Strhodes						    ISC_MSG_NONE, "\tNone.\n"));
1222135446Strhodes	}
1223135446Strhodes}
1224135446Strhodes#endif
1225135446Strhodes
1226135446Strhodes/*
1227135446Strhodes * Print the stats[] on the stream "out" with suitable formatting.
1228135446Strhodes */
1229135446Strhodesvoid
1230135446Strhodesisc_mem_stats(isc_mem_t *ctx, FILE *out) {
1231135446Strhodes	size_t i;
1232135446Strhodes	const struct stats *s;
1233135446Strhodes	const isc_mempool_t *pool;
1234135446Strhodes
1235135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1236170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1237135446Strhodes
1238135446Strhodes	for (i = 0; i <= ctx->max_size; i++) {
1239135446Strhodes		s = &ctx->stats[i];
1240135446Strhodes
1241135446Strhodes		if (s->totalgets == 0U && s->gets == 0U)
1242135446Strhodes			continue;
1243135446Strhodes		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
1244135446Strhodes			(i == ctx->max_size) ? ">=" : "  ",
1245135446Strhodes			(unsigned long) i, s->totalgets, s->gets);
1246170222Sdougb		if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
1247170222Sdougb		    (s->blocks != 0U || s->freefrags != 0U))
1248135446Strhodes			fprintf(out, " (%lu bl, %lu ff)",
1249135446Strhodes				s->blocks, s->freefrags);
1250135446Strhodes		fputc('\n', out);
1251135446Strhodes	}
1252135446Strhodes
1253135446Strhodes	/*
1254135446Strhodes	 * Note that since a pool can be locked now, these stats might be
1255135446Strhodes	 * somewhat off if the pool is in active use at the time the stats
1256135446Strhodes	 * are dumped.  The link fields are protected by the isc_mem_t's
1257135446Strhodes	 * lock, however, so walking this list and extracting integers from
1258135446Strhodes	 * stats fields is always safe.
1259135446Strhodes	 */
1260135446Strhodes	pool = ISC_LIST_HEAD(ctx->pools);
1261135446Strhodes	if (pool != NULL) {
1262193149Sdougb		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1263135446Strhodes					    ISC_MSG_POOLSTATS,
1264135446Strhodes					    "[Pool statistics]\n"));
1265135446Strhodes		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
1266135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1267135446Strhodes				       ISC_MSG_POOLNAME, "name"),
1268135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1269135446Strhodes				       ISC_MSG_POOLSIZE, "size"),
1270135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1271135446Strhodes				       ISC_MSG_POOLMAXALLOC, "maxalloc"),
1272135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1273135446Strhodes				       ISC_MSG_POOLALLOCATED, "allocated"),
1274135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1275135446Strhodes				       ISC_MSG_POOLFREECOUNT, "freecount"),
1276135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1277135446Strhodes				       ISC_MSG_POOLFREEMAX, "freemax"),
1278135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1279135446Strhodes				       ISC_MSG_POOLFILLCOUNT, "fillcount"),
1280135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1281135446Strhodes				       ISC_MSG_POOLGETS, "gets"),
1282135446Strhodes			"L");
1283135446Strhodes	}
1284135446Strhodes	while (pool != NULL) {
1285135446Strhodes		fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
1286135446Strhodes			pool->name, (unsigned long) pool->size, pool->maxalloc,
1287135446Strhodes			pool->allocated, pool->freecount, pool->freemax,
1288135446Strhodes			pool->fillcount, pool->gets,
1289135446Strhodes			(pool->lock == NULL ? "N" : "Y"));
1290135446Strhodes		pool = ISC_LIST_NEXT(pool, link);
1291135446Strhodes	}
1292135446Strhodes
1293135446Strhodes#if ISC_MEM_TRACKLINES
1294135446Strhodes	print_active(ctx, out);
1295135446Strhodes#endif
1296135446Strhodes
1297170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1298135446Strhodes}
1299135446Strhodes
1300135446Strhodes/*
1301135446Strhodes * Replacements for malloc() and free() -- they implicitly remember the
1302135446Strhodes * size of the object allocated (with some additional overhead).
1303135446Strhodes */
1304135446Strhodes
1305135446Strhodesstatic void *
1306135446Strhodesisc__mem_allocateunlocked(isc_mem_t *ctx, size_t size) {
1307135446Strhodes	size_info *si;
1308135446Strhodes
1309135446Strhodes	size += ALIGNMENT_SIZE;
1310170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1311170222Sdougb		size += ALIGNMENT_SIZE;
1312170222Sdougb
1313170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
1314170222Sdougb		si = mem_getunlocked(ctx, size);
1315170222Sdougb	else
1316170222Sdougb		si = mem_get(ctx, size);
1317170222Sdougb
1318135446Strhodes	if (si == NULL)
1319135446Strhodes		return (NULL);
1320170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1321170222Sdougb		si->u.ctx = ctx;
1322170222Sdougb		si++;
1323170222Sdougb	}
1324135446Strhodes	si->u.size = size;
1325135446Strhodes	return (&si[1]);
1326135446Strhodes}
1327135446Strhodes
1328135446Strhodesvoid *
1329135446Strhodesisc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
1330135446Strhodes	size_info *si;
1331170222Sdougb	isc_boolean_t call_water = ISC_FALSE;
1332135446Strhodes
1333135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1334135446Strhodes
1335170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1336170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1337170222Sdougb		si = isc__mem_allocateunlocked(ctx, size);
1338170222Sdougb	} else {
1339170222Sdougb		si = isc__mem_allocateunlocked(ctx, size);
1340170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1341170222Sdougb		if (si != NULL)
1342170222Sdougb			mem_getstats(ctx, si[-1].u.size);
1343170222Sdougb	}
1344135446Strhodes
1345135446Strhodes#if ISC_MEM_TRACKLINES
1346135446Strhodes	ADD_TRACE(ctx, si, si[-1].u.size, file, line);
1347135446Strhodes#endif
1348170222Sdougb	if (ctx->hi_water != 0U && !ctx->hi_called &&
1349170222Sdougb	    ctx->inuse > ctx->hi_water) {
1350170222Sdougb		ctx->hi_called = ISC_TRUE;
1351170222Sdougb		call_water = ISC_TRUE;
1352170222Sdougb	}
1353170222Sdougb	if (ctx->inuse > ctx->maxinuse) {
1354170222Sdougb		ctx->maxinuse = ctx->inuse;
1355170222Sdougb		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1356170222Sdougb		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1357170222Sdougb			fprintf(stderr, "maxinuse = %lu\n",
1358170222Sdougb				(unsigned long)ctx->inuse);
1359170222Sdougb	}
1360170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1361135446Strhodes
1362170222Sdougb	if (call_water)
1363170222Sdougb		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1364135446Strhodes
1365135446Strhodes	return (si);
1366135446Strhodes}
1367135446Strhodes
1368193149Sdougbvoid *
1369193149Sdougbisc__mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG) {
1370193149Sdougb	void *new_ptr = NULL;
1371193149Sdougb	size_t oldsize, copysize;
1372193149Sdougb
1373193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1374193149Sdougb
1375193149Sdougb	/*
1376193149Sdougb	 * This function emulates the realloc(3) standard library function:
1377193149Sdougb	 * - if size > 0, allocate new memory; and if ptr is non NULL, copy
1378193149Sdougb	 *   as much of the old contents to the new buffer and free the old one.
1379193149Sdougb	 *   Note that when allocation fails the original pointer is intact;
1380193149Sdougb	 *   the caller must free it.
1381193149Sdougb	 * - if size is 0 and ptr is non NULL, simply free the given ptr.
1382193149Sdougb	 * - this function returns:
1383193149Sdougb	 *     pointer to the newly allocated memory, or
1384193149Sdougb	 *     NULL if allocation fails or doesn't happen.
1385193149Sdougb	 */
1386193149Sdougb	if (size > 0U) {
1387193149Sdougb		new_ptr = isc__mem_allocate(ctx, size FLARG_PASS);
1388193149Sdougb		if (new_ptr != NULL && ptr != NULL) {
1389193149Sdougb			oldsize = (((size_info *)ptr)[-1]).u.size;
1390193149Sdougb			INSIST(oldsize >= ALIGNMENT_SIZE);
1391193149Sdougb			oldsize -= ALIGNMENT_SIZE;
1392193149Sdougb			copysize = oldsize > size ? size : oldsize;
1393193149Sdougb			memcpy(new_ptr, ptr, copysize);
1394193149Sdougb			isc__mem_free(ctx, ptr FLARG_PASS);
1395193149Sdougb		}
1396193149Sdougb	} else if (ptr != NULL)
1397193149Sdougb		isc__mem_free(ctx, ptr FLARG_PASS);
1398193149Sdougb
1399193149Sdougb	return (new_ptr);
1400193149Sdougb}
1401193149Sdougb
1402135446Strhodesvoid
1403135446Strhodesisc__mem_free(isc_mem_t *ctx, void *ptr FLARG) {
1404135446Strhodes	size_info *si;
1405135446Strhodes	size_t size;
1406170222Sdougb	isc_boolean_t call_water= ISC_FALSE;
1407135446Strhodes
1408135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1409135446Strhodes	REQUIRE(ptr != NULL);
1410135446Strhodes
1411170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1412170222Sdougb		si = &(((size_info *)ptr)[-2]);
1413170222Sdougb		REQUIRE(si->u.ctx == ctx);
1414170222Sdougb		size = si[1].u.size;
1415170222Sdougb	} else {
1416170222Sdougb		si = &(((size_info *)ptr)[-1]);
1417170222Sdougb		size = si->u.size;
1418170222Sdougb	}
1419135446Strhodes
1420170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1421170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1422170222Sdougb		mem_putunlocked(ctx, si, size);
1423170222Sdougb	} else {
1424170222Sdougb		mem_put(ctx, si, size);
1425170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1426170222Sdougb		mem_putstats(ctx, si, size);
1427170222Sdougb	}
1428135446Strhodes
1429135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1430135446Strhodes
1431170222Sdougb	/*
1432170222Sdougb	 * The check against ctx->lo_water == 0 is for the condition
1433170222Sdougb	 * when the context was pushed over hi_water but then had
1434170222Sdougb	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1435170222Sdougb	 */
1436186462Sdougb	if (ctx->hi_called &&
1437170222Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1438170222Sdougb		ctx->hi_called = ISC_FALSE;
1439170222Sdougb
1440170222Sdougb		if (ctx->water != NULL)
1441170222Sdougb			call_water = ISC_TRUE;
1442170222Sdougb	}
1443170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1444170222Sdougb
1445170222Sdougb	if (call_water)
1446170222Sdougb		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1447135446Strhodes}
1448135446Strhodes
1449135446Strhodes
1450135446Strhodes/*
1451135446Strhodes * Other useful things.
1452135446Strhodes */
1453135446Strhodes
1454135446Strhodeschar *
1455135446Strhodesisc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
1456135446Strhodes	size_t len;
1457135446Strhodes	char *ns;
1458135446Strhodes
1459135446Strhodes	REQUIRE(VALID_CONTEXT(mctx));
1460135446Strhodes	REQUIRE(s != NULL);
1461135446Strhodes
1462135446Strhodes	len = strlen(s);
1463135446Strhodes
1464135446Strhodes	ns = isc__mem_allocate(mctx, len + 1 FLARG_PASS);
1465135446Strhodes
1466135446Strhodes	if (ns != NULL)
1467135446Strhodes		strncpy(ns, s, len + 1);
1468135446Strhodes
1469135446Strhodes	return (ns);
1470135446Strhodes}
1471135446Strhodes
1472135446Strhodesvoid
1473135446Strhodesisc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) {
1474135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1475170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1476135446Strhodes
1477135446Strhodes	ctx->checkfree = flag;
1478135446Strhodes
1479170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1480135446Strhodes}
1481135446Strhodes
1482135446Strhodes/*
1483135446Strhodes * Quotas
1484135446Strhodes */
1485135446Strhodes
1486135446Strhodesvoid
1487135446Strhodesisc_mem_setquota(isc_mem_t *ctx, size_t quota) {
1488135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1489170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1490135446Strhodes
1491135446Strhodes	ctx->quota = quota;
1492135446Strhodes
1493170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1494135446Strhodes}
1495135446Strhodes
1496135446Strhodessize_t
1497135446Strhodesisc_mem_getquota(isc_mem_t *ctx) {
1498135446Strhodes	size_t quota;
1499135446Strhodes
1500135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1501170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1502135446Strhodes
1503135446Strhodes	quota = ctx->quota;
1504135446Strhodes
1505170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1506135446Strhodes
1507135446Strhodes	return (quota);
1508135446Strhodes}
1509135446Strhodes
1510135446Strhodessize_t
1511135446Strhodesisc_mem_inuse(isc_mem_t *ctx) {
1512135446Strhodes	size_t inuse;
1513135446Strhodes
1514135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1515170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1516135446Strhodes
1517135446Strhodes	inuse = ctx->inuse;
1518135446Strhodes
1519170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1520135446Strhodes
1521135446Strhodes	return (inuse);
1522135446Strhodes}
1523135446Strhodes
1524135446Strhodesvoid
1525135446Strhodesisc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
1526186462Sdougb		 size_t hiwater, size_t lowater)
1527135446Strhodes{
1528170222Sdougb	isc_boolean_t callwater = ISC_FALSE;
1529170222Sdougb	isc_mem_water_t oldwater;
1530170222Sdougb	void *oldwater_arg;
1531170222Sdougb
1532135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1533135446Strhodes	REQUIRE(hiwater >= lowater);
1534135446Strhodes
1535170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1536170222Sdougb	oldwater = ctx->water;
1537170222Sdougb	oldwater_arg = ctx->water_arg;
1538135446Strhodes	if (water == NULL) {
1539170222Sdougb		callwater = ctx->hi_called;
1540135446Strhodes		ctx->water = NULL;
1541135446Strhodes		ctx->water_arg = NULL;
1542135446Strhodes		ctx->hi_water = 0;
1543135446Strhodes		ctx->lo_water = 0;
1544135446Strhodes		ctx->hi_called = ISC_FALSE;
1545135446Strhodes	} else {
1546170222Sdougb		if (ctx->hi_called &&
1547170222Sdougb		    (ctx->water != water || ctx->water_arg != water_arg ||
1548170222Sdougb		     ctx->inuse < lowater || lowater == 0U))
1549170222Sdougb			callwater = ISC_TRUE;
1550135446Strhodes		ctx->water = water;
1551135446Strhodes		ctx->water_arg = water_arg;
1552135446Strhodes		ctx->hi_water = hiwater;
1553135446Strhodes		ctx->lo_water = lowater;
1554135446Strhodes		ctx->hi_called = ISC_FALSE;
1555135446Strhodes	}
1556170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1557186462Sdougb
1558170222Sdougb	if (callwater && oldwater != NULL)
1559170222Sdougb		(oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1560135446Strhodes}
1561135446Strhodes
1562193149Sdougbvoid
1563193149Sdougbisc_mem_setname(isc_mem_t *ctx, const char *name, void *tag) {
1564193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1565193149Sdougb
1566193149Sdougb	LOCK(&ctx->lock);
1567193149Sdougb	memset(ctx->name, 0, sizeof(ctx->name));
1568193149Sdougb	strncpy(ctx->name, name, sizeof(ctx->name) - 1);
1569193149Sdougb	ctx->tag = tag;
1570193149Sdougb	UNLOCK(&ctx->lock);
1571193149Sdougb}
1572193149Sdougb
1573193149Sdougbconst char *
1574193149Sdougbisc_mem_getname(isc_mem_t *ctx) {
1575193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1576193149Sdougb
1577193149Sdougb	return (ctx->name);
1578193149Sdougb}
1579193149Sdougb
1580193149Sdougbvoid *
1581193149Sdougbisc_mem_gettag(isc_mem_t *ctx) {
1582193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1583193149Sdougb
1584193149Sdougb	return (ctx->tag);
1585193149Sdougb}
1586193149Sdougb
1587135446Strhodes/*
1588135446Strhodes * Memory pool stuff
1589135446Strhodes */
1590135446Strhodes
1591135446Strhodesisc_result_t
1592135446Strhodesisc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) {
1593135446Strhodes	isc_mempool_t *mpctx;
1594135446Strhodes
1595135446Strhodes	REQUIRE(VALID_CONTEXT(mctx));
1596135446Strhodes	REQUIRE(size > 0U);
1597135446Strhodes	REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1598135446Strhodes
1599135446Strhodes	/*
1600135446Strhodes	 * Allocate space for this pool, initialize values, and if all works
1601135446Strhodes	 * well, attach to the memory context.
1602135446Strhodes	 */
1603135446Strhodes	mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
1604135446Strhodes	if (mpctx == NULL)
1605135446Strhodes		return (ISC_R_NOMEMORY);
1606135446Strhodes
1607135446Strhodes	mpctx->magic = MEMPOOL_MAGIC;
1608135446Strhodes	mpctx->lock = NULL;
1609135446Strhodes	mpctx->mctx = mctx;
1610135446Strhodes	mpctx->size = size;
1611135446Strhodes	mpctx->maxalloc = UINT_MAX;
1612135446Strhodes	mpctx->allocated = 0;
1613135446Strhodes	mpctx->freecount = 0;
1614135446Strhodes	mpctx->freemax = 1;
1615135446Strhodes	mpctx->fillcount = 1;
1616135446Strhodes	mpctx->gets = 0;
1617135446Strhodes#if ISC_MEMPOOL_NAMES
1618135446Strhodes	mpctx->name[0] = 0;
1619135446Strhodes#endif
1620135446Strhodes	mpctx->items = NULL;
1621135446Strhodes
1622135446Strhodes	*mpctxp = mpctx;
1623135446Strhodes
1624170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1625135446Strhodes	ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1626193149Sdougb	mctx->poolcnt++;
1627170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1628135446Strhodes
1629135446Strhodes	return (ISC_R_SUCCESS);
1630135446Strhodes}
1631135446Strhodes
1632135446Strhodesvoid
1633135446Strhodesisc_mempool_setname(isc_mempool_t *mpctx, const char *name) {
1634135446Strhodes	REQUIRE(name != NULL);
1635135446Strhodes
1636135446Strhodes#if ISC_MEMPOOL_NAMES
1637135446Strhodes	if (mpctx->lock != NULL)
1638135446Strhodes		LOCK(mpctx->lock);
1639135446Strhodes
1640135446Strhodes	strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
1641135446Strhodes	mpctx->name[sizeof(mpctx->name) - 1] = '\0';
1642135446Strhodes
1643135446Strhodes	if (mpctx->lock != NULL)
1644135446Strhodes		UNLOCK(mpctx->lock);
1645135446Strhodes#else
1646135446Strhodes	UNUSED(mpctx);
1647135446Strhodes	UNUSED(name);
1648135446Strhodes#endif
1649135446Strhodes}
1650135446Strhodes
1651135446Strhodesvoid
1652135446Strhodesisc_mempool_destroy(isc_mempool_t **mpctxp) {
1653135446Strhodes	isc_mempool_t *mpctx;
1654135446Strhodes	isc_mem_t *mctx;
1655135446Strhodes	isc_mutex_t *lock;
1656135446Strhodes	element *item;
1657135446Strhodes
1658135446Strhodes	REQUIRE(mpctxp != NULL);
1659135446Strhodes	mpctx = *mpctxp;
1660135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1661135446Strhodes#if ISC_MEMPOOL_NAMES
1662135446Strhodes	if (mpctx->allocated > 0)
1663135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
1664135446Strhodes				 "isc_mempool_destroy(): mempool %s "
1665135446Strhodes				 "leaked memory",
1666135446Strhodes				 mpctx->name);
1667135446Strhodes#endif
1668135446Strhodes	REQUIRE(mpctx->allocated == 0);
1669135446Strhodes
1670135446Strhodes	mctx = mpctx->mctx;
1671135446Strhodes
1672135446Strhodes	lock = mpctx->lock;
1673135446Strhodes
1674135446Strhodes	if (lock != NULL)
1675135446Strhodes		LOCK(lock);
1676135446Strhodes
1677135446Strhodes	/*
1678135446Strhodes	 * Return any items on the free list
1679135446Strhodes	 */
1680170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1681135446Strhodes	while (mpctx->items != NULL) {
1682135446Strhodes		INSIST(mpctx->freecount > 0);
1683135446Strhodes		mpctx->freecount--;
1684135446Strhodes		item = mpctx->items;
1685135446Strhodes		mpctx->items = item->next;
1686135446Strhodes
1687170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1688170222Sdougb			mem_putunlocked(mctx, item, mpctx->size);
1689170222Sdougb		} else {
1690170222Sdougb			mem_put(mctx, item, mpctx->size);
1691170222Sdougb			mem_putstats(mctx, item, mpctx->size);
1692170222Sdougb		}
1693135446Strhodes	}
1694170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1695135446Strhodes
1696135446Strhodes	/*
1697135446Strhodes	 * Remove our linked list entry from the memory context.
1698135446Strhodes	 */
1699170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1700135446Strhodes	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1701193149Sdougb	mctx->poolcnt--;
1702170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1703135446Strhodes
1704135446Strhodes	mpctx->magic = 0;
1705135446Strhodes
1706135446Strhodes	isc_mem_put(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
1707135446Strhodes
1708135446Strhodes	if (lock != NULL)
1709135446Strhodes		UNLOCK(lock);
1710135446Strhodes
1711135446Strhodes	*mpctxp = NULL;
1712135446Strhodes}
1713135446Strhodes
1714135446Strhodesvoid
1715135446Strhodesisc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock) {
1716135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1717135446Strhodes	REQUIRE(mpctx->lock == NULL);
1718135446Strhodes	REQUIRE(lock != NULL);
1719135446Strhodes
1720135446Strhodes	mpctx->lock = lock;
1721135446Strhodes}
1722135446Strhodes
1723135446Strhodesvoid *
1724135446Strhodesisc__mempool_get(isc_mempool_t *mpctx FLARG) {
1725135446Strhodes	element *item;
1726135446Strhodes	isc_mem_t *mctx;
1727135446Strhodes	unsigned int i;
1728135446Strhodes
1729135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1730135446Strhodes
1731135446Strhodes	mctx = mpctx->mctx;
1732135446Strhodes
1733135446Strhodes	if (mpctx->lock != NULL)
1734135446Strhodes		LOCK(mpctx->lock);
1735135446Strhodes
1736135446Strhodes	/*
1737135446Strhodes	 * Don't let the caller go over quota
1738135446Strhodes	 */
1739135446Strhodes	if (mpctx->allocated >= mpctx->maxalloc) {
1740135446Strhodes		item = NULL;
1741135446Strhodes		goto out;
1742135446Strhodes	}
1743135446Strhodes
1744135446Strhodes	/*
1745135446Strhodes	 * if we have a free list item, return the first here
1746135446Strhodes	 */
1747135446Strhodes	item = mpctx->items;
1748135446Strhodes	if (item != NULL) {
1749135446Strhodes		mpctx->items = item->next;
1750135446Strhodes		INSIST(mpctx->freecount > 0);
1751135446Strhodes		mpctx->freecount--;
1752135446Strhodes		mpctx->gets++;
1753135446Strhodes		mpctx->allocated++;
1754135446Strhodes		goto out;
1755135446Strhodes	}
1756135446Strhodes
1757135446Strhodes	/*
1758135446Strhodes	 * We need to dip into the well.  Lock the memory context here and
1759135446Strhodes	 * fill up our free list.
1760135446Strhodes	 */
1761170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1762135446Strhodes	for (i = 0; i < mpctx->fillcount; i++) {
1763170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1764170222Sdougb			item = mem_getunlocked(mctx, mpctx->size);
1765170222Sdougb		} else {
1766170222Sdougb			item = mem_get(mctx, mpctx->size);
1767170222Sdougb			if (item != NULL)
1768170222Sdougb				mem_getstats(mctx, mpctx->size);
1769170222Sdougb		}
1770135446Strhodes		if (item == NULL)
1771135446Strhodes			break;
1772135446Strhodes		item->next = mpctx->items;
1773135446Strhodes		mpctx->items = item;
1774135446Strhodes		mpctx->freecount++;
1775135446Strhodes	}
1776170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1777135446Strhodes
1778135446Strhodes	/*
1779135446Strhodes	 * If we didn't get any items, return NULL.
1780135446Strhodes	 */
1781135446Strhodes	item = mpctx->items;
1782135446Strhodes	if (item == NULL)
1783135446Strhodes		goto out;
1784135446Strhodes
1785135446Strhodes	mpctx->items = item->next;
1786135446Strhodes	mpctx->freecount--;
1787135446Strhodes	mpctx->gets++;
1788135446Strhodes	mpctx->allocated++;
1789135446Strhodes
1790135446Strhodes out:
1791135446Strhodes	if (mpctx->lock != NULL)
1792135446Strhodes		UNLOCK(mpctx->lock);
1793135446Strhodes
1794135446Strhodes#if ISC_MEM_TRACKLINES
1795135446Strhodes	if (item != NULL) {
1796170222Sdougb		MCTXLOCK(mctx, &mctx->lock);
1797135446Strhodes		ADD_TRACE(mctx, item, mpctx->size, file, line);
1798170222Sdougb		MCTXUNLOCK(mctx, &mctx->lock);
1799135446Strhodes	}
1800135446Strhodes#endif /* ISC_MEM_TRACKLINES */
1801135446Strhodes
1802135446Strhodes	return (item);
1803135446Strhodes}
1804135446Strhodes
1805135446Strhodesvoid
1806135446Strhodesisc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) {
1807135446Strhodes	isc_mem_t *mctx;
1808135446Strhodes	element *item;
1809135446Strhodes
1810135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1811135446Strhodes	REQUIRE(mem != NULL);
1812135446Strhodes
1813135446Strhodes	mctx = mpctx->mctx;
1814135446Strhodes
1815135446Strhodes	if (mpctx->lock != NULL)
1816135446Strhodes		LOCK(mpctx->lock);
1817135446Strhodes
1818135446Strhodes	INSIST(mpctx->allocated > 0);
1819135446Strhodes	mpctx->allocated--;
1820135446Strhodes
1821135446Strhodes#if ISC_MEM_TRACKLINES
1822170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1823135446Strhodes	DELETE_TRACE(mctx, mem, mpctx->size, file, line);
1824170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1825135446Strhodes#endif /* ISC_MEM_TRACKLINES */
1826135446Strhodes
1827135446Strhodes	/*
1828135446Strhodes	 * If our free list is full, return this to the mctx directly.
1829135446Strhodes	 */
1830135446Strhodes	if (mpctx->freecount >= mpctx->freemax) {
1831170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1832170222Sdougb			MCTXLOCK(mctx, &mctx->lock);
1833170222Sdougb			mem_putunlocked(mctx, mem, mpctx->size);
1834170222Sdougb			MCTXUNLOCK(mctx, &mctx->lock);
1835170222Sdougb		} else {
1836170222Sdougb			mem_put(mctx, mem, mpctx->size);
1837170222Sdougb			MCTXLOCK(mctx, &mctx->lock);
1838170222Sdougb			mem_putstats(mctx, mem, mpctx->size);
1839170222Sdougb			MCTXUNLOCK(mctx, &mctx->lock);
1840170222Sdougb		}
1841135446Strhodes		if (mpctx->lock != NULL)
1842135446Strhodes			UNLOCK(mpctx->lock);
1843135446Strhodes		return;
1844135446Strhodes	}
1845135446Strhodes
1846135446Strhodes	/*
1847135446Strhodes	 * Otherwise, attach it to our free list and bump the counter.
1848135446Strhodes	 */
1849135446Strhodes	mpctx->freecount++;
1850135446Strhodes	item = (element *)mem;
1851135446Strhodes	item->next = mpctx->items;
1852135446Strhodes	mpctx->items = item;
1853135446Strhodes
1854135446Strhodes	if (mpctx->lock != NULL)
1855135446Strhodes		UNLOCK(mpctx->lock);
1856135446Strhodes}
1857135446Strhodes
1858135446Strhodes/*
1859135446Strhodes * Quotas
1860135446Strhodes */
1861135446Strhodes
1862135446Strhodesvoid
1863135446Strhodesisc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit) {
1864135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1865135446Strhodes
1866135446Strhodes	if (mpctx->lock != NULL)
1867135446Strhodes		LOCK(mpctx->lock);
1868135446Strhodes
1869135446Strhodes	mpctx->freemax = limit;
1870135446Strhodes
1871135446Strhodes	if (mpctx->lock != NULL)
1872135446Strhodes		UNLOCK(mpctx->lock);
1873135446Strhodes}
1874135446Strhodes
1875135446Strhodesunsigned int
1876135446Strhodesisc_mempool_getfreemax(isc_mempool_t *mpctx) {
1877135446Strhodes	unsigned int freemax;
1878135446Strhodes
1879135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1880135446Strhodes
1881135446Strhodes	if (mpctx->lock != NULL)
1882135446Strhodes		LOCK(mpctx->lock);
1883135446Strhodes
1884135446Strhodes	freemax = mpctx->freemax;
1885135446Strhodes
1886135446Strhodes	if (mpctx->lock != NULL)
1887135446Strhodes		UNLOCK(mpctx->lock);
1888135446Strhodes
1889135446Strhodes	return (freemax);
1890135446Strhodes}
1891135446Strhodes
1892135446Strhodesunsigned int
1893135446Strhodesisc_mempool_getfreecount(isc_mempool_t *mpctx) {
1894135446Strhodes	unsigned int freecount;
1895135446Strhodes
1896135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1897135446Strhodes
1898135446Strhodes	if (mpctx->lock != NULL)
1899135446Strhodes		LOCK(mpctx->lock);
1900135446Strhodes
1901135446Strhodes	freecount = mpctx->freecount;
1902135446Strhodes
1903135446Strhodes	if (mpctx->lock != NULL)
1904135446Strhodes		UNLOCK(mpctx->lock);
1905135446Strhodes
1906135446Strhodes	return (freecount);
1907135446Strhodes}
1908135446Strhodes
1909135446Strhodesvoid
1910135446Strhodesisc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit) {
1911135446Strhodes	REQUIRE(limit > 0);
1912135446Strhodes
1913135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1914135446Strhodes
1915135446Strhodes	if (mpctx->lock != NULL)
1916135446Strhodes		LOCK(mpctx->lock);
1917135446Strhodes
1918135446Strhodes	mpctx->maxalloc = limit;
1919135446Strhodes
1920135446Strhodes	if (mpctx->lock != NULL)
1921135446Strhodes		UNLOCK(mpctx->lock);
1922135446Strhodes}
1923135446Strhodes
1924135446Strhodesunsigned int
1925135446Strhodesisc_mempool_getmaxalloc(isc_mempool_t *mpctx) {
1926135446Strhodes	unsigned int maxalloc;
1927135446Strhodes
1928135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1929135446Strhodes
1930135446Strhodes	if (mpctx->lock != NULL)
1931135446Strhodes		LOCK(mpctx->lock);
1932135446Strhodes
1933135446Strhodes	maxalloc = mpctx->maxalloc;
1934135446Strhodes
1935135446Strhodes	if (mpctx->lock != NULL)
1936135446Strhodes		UNLOCK(mpctx->lock);
1937135446Strhodes
1938135446Strhodes	return (maxalloc);
1939135446Strhodes}
1940135446Strhodes
1941135446Strhodesunsigned int
1942135446Strhodesisc_mempool_getallocated(isc_mempool_t *mpctx) {
1943135446Strhodes	unsigned int allocated;
1944135446Strhodes
1945135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1946135446Strhodes
1947135446Strhodes	if (mpctx->lock != NULL)
1948135446Strhodes		LOCK(mpctx->lock);
1949135446Strhodes
1950135446Strhodes	allocated = mpctx->allocated;
1951135446Strhodes
1952135446Strhodes	if (mpctx->lock != NULL)
1953135446Strhodes		UNLOCK(mpctx->lock);
1954135446Strhodes
1955135446Strhodes	return (allocated);
1956135446Strhodes}
1957135446Strhodes
1958135446Strhodesvoid
1959135446Strhodesisc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit) {
1960135446Strhodes	REQUIRE(limit > 0);
1961135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1962135446Strhodes
1963135446Strhodes	if (mpctx->lock != NULL)
1964135446Strhodes		LOCK(mpctx->lock);
1965135446Strhodes
1966135446Strhodes	mpctx->fillcount = limit;
1967135446Strhodes
1968135446Strhodes	if (mpctx->lock != NULL)
1969135446Strhodes		UNLOCK(mpctx->lock);
1970135446Strhodes}
1971135446Strhodes
1972135446Strhodesunsigned int
1973135446Strhodesisc_mempool_getfillcount(isc_mempool_t *mpctx) {
1974135446Strhodes	unsigned int fillcount;
1975135446Strhodes
1976135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1977135446Strhodes
1978135446Strhodes	if (mpctx->lock != NULL)
1979135446Strhodes		LOCK(mpctx->lock);
1980135446Strhodes
1981135446Strhodes	fillcount = mpctx->fillcount;
1982135446Strhodes
1983135446Strhodes	if (mpctx->lock != NULL)
1984135446Strhodes		UNLOCK(mpctx->lock);
1985135446Strhodes
1986135446Strhodes	return (fillcount);
1987135446Strhodes}
1988170222Sdougb
1989170222Sdougbvoid
1990170222Sdougbisc_mem_printactive(isc_mem_t *ctx, FILE *file) {
1991170222Sdougb
1992170222Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1993170222Sdougb	REQUIRE(file != NULL);
1994170222Sdougb
1995170222Sdougb#if !ISC_MEM_TRACKLINES
1996170222Sdougb	UNUSED(ctx);
1997170222Sdougb	UNUSED(file);
1998170222Sdougb#else
1999170222Sdougb	print_active(ctx, file);
2000170222Sdougb#endif
2001170222Sdougb}
2002170222Sdougb
2003170222Sdougbvoid
2004170222Sdougbisc_mem_printallactive(FILE *file) {
2005170222Sdougb#if !ISC_MEM_TRACKLINES
2006170222Sdougb	UNUSED(file);
2007170222Sdougb#else
2008170222Sdougb	isc_mem_t *ctx;
2009170222Sdougb
2010170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2011170222Sdougb
2012170222Sdougb	LOCK(&lock);
2013170222Sdougb	for (ctx = ISC_LIST_HEAD(contexts);
2014170222Sdougb	     ctx != NULL;
2015170222Sdougb	     ctx = ISC_LIST_NEXT(ctx, link)) {
2016170222Sdougb		fprintf(file, "context: %p\n", ctx);
2017170222Sdougb		print_active(ctx, file);
2018170222Sdougb	}
2019170222Sdougb	UNLOCK(&lock);
2020170222Sdougb#endif
2021170222Sdougb}
2022170222Sdougb
2023186462Sdougbvoid
2024170222Sdougbisc_mem_checkdestroyed(FILE *file) {
2025170222Sdougb
2026170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2027170222Sdougb
2028170222Sdougb	LOCK(&lock);
2029170222Sdougb	if (!ISC_LIST_EMPTY(contexts))  {
2030170222Sdougb#if ISC_MEM_TRACKLINES
2031170222Sdougb		isc_mem_t *ctx;
2032170222Sdougb
2033170222Sdougb		for (ctx = ISC_LIST_HEAD(contexts);
2034170222Sdougb		     ctx != NULL;
2035170222Sdougb		     ctx = ISC_LIST_NEXT(ctx, link)) {
2036170222Sdougb			fprintf(file, "context: %p\n", ctx);
2037170222Sdougb			print_active(ctx, file);
2038170222Sdougb		}
2039170222Sdougb		fflush(file);
2040170222Sdougb#endif
2041174187Sdougb		INSIST(0);
2042170222Sdougb	}
2043170222Sdougb	UNLOCK(&lock);
2044170222Sdougb}
2045193149Sdougb
2046193149Sdougbunsigned int
2047193149Sdougbisc_mem_references(isc_mem_t *ctx) {
2048193149Sdougb	unsigned int references;
2049193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2050193149Sdougb
2051193149Sdougb	MCTXLOCK(ctx, &ctx->lock);
2052193149Sdougb	references = ctx->references;
2053193149Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
2054193149Sdougb
2055193149Sdougb	return (references);
2056193149Sdougb}
2057193149Sdougb
2058193149Sdougb#ifdef HAVE_LIBXML2
2059193149Sdougb
2060193149Sdougbtypedef struct summarystat {
2061193149Sdougb	isc_uint64_t	total;
2062193149Sdougb	isc_uint64_t	inuse;
2063193149Sdougb	isc_uint64_t	blocksize;
2064193149Sdougb	isc_uint64_t	contextsize;
2065193149Sdougb} summarystat_t;
2066193149Sdougb
2067193149Sdougbstatic void
2068193149Sdougbrenderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
2069193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2070193149Sdougb
2071193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "context");
2072193149Sdougb
2073193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
2074193149Sdougb	xmlTextWriterWriteFormatString(writer, "%p", ctx);
2075193149Sdougb	xmlTextWriterEndElement(writer); /* id */
2076193149Sdougb
2077193149Sdougb	if (ctx->name[0] != 0) {
2078193149Sdougb		xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
2079193149Sdougb		xmlTextWriterWriteFormatString(writer, "%s", ctx->name);
2080193149Sdougb		xmlTextWriterEndElement(writer); /* name */
2081193149Sdougb	}
2082193149Sdougb
2083193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2084193149Sdougb	MCTXLOCK(ctx, &ctx->lock);
2085193149Sdougb
2086193149Sdougb	summary->contextsize += sizeof(*ctx) +
2087193149Sdougb		(ctx->max_size + 1) * sizeof(struct stats) +
2088193149Sdougb		ctx->max_size * sizeof(element *) +
2089193149Sdougb		ctx->basic_table_count * sizeof(char *);
2090193149Sdougb#if ISC_MEM_TRACKLINES
2091193149Sdougb	if (ctx->debuglist != NULL) {
2092193149Sdougb		summary->contextsize +=
2093193149Sdougb			(ctx->max_size + 1) * sizeof(debuglist_t) +
2094193149Sdougb			ctx->debuglistcnt * sizeof(debuglink_t);
2095193149Sdougb	}
2096193149Sdougb#endif
2097193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
2098193149Sdougb	xmlTextWriterWriteFormatString(writer, "%d", ctx->references);
2099193149Sdougb	xmlTextWriterEndElement(writer); /* references */
2100193149Sdougb
2101193149Sdougb	summary->total += ctx->total;
2102193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "total");
2103193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2104193149Sdougb				       (isc_uint64_t)ctx->total);
2105193149Sdougb	xmlTextWriterEndElement(writer); /* total */
2106193149Sdougb
2107193149Sdougb	summary->inuse += ctx->inuse;
2108193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse");
2109193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2110193149Sdougb				       (isc_uint64_t)ctx->inuse);
2111193149Sdougb	xmlTextWriterEndElement(writer); /* inuse */
2112193149Sdougb
2113193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse");
2114193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2115193149Sdougb				       (isc_uint64_t)ctx->maxinuse);
2116193149Sdougb	xmlTextWriterEndElement(writer); /* maxinuse */
2117193149Sdougb
2118193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize");
2119193149Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2120193149Sdougb		summary->blocksize += ctx->basic_table_count *
2121193149Sdougb			NUM_BASIC_BLOCKS * ctx->mem_target;
2122193149Sdougb		xmlTextWriterWriteFormatString(writer,
2123193149Sdougb					       "%" ISC_PRINT_QUADFORMAT "u",
2124193149Sdougb					       (isc_uint64_t)
2125193149Sdougb					       ctx->basic_table_count *
2126193149Sdougb					       NUM_BASIC_BLOCKS *
2127193149Sdougb					       ctx->mem_target);
2128193149Sdougb	} else
2129193149Sdougb		xmlTextWriterWriteFormatString(writer, "%s", "-");
2130193149Sdougb	xmlTextWriterEndElement(writer); /* blocksize */
2131193149Sdougb
2132193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools");
2133193149Sdougb	xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt);
2134193149Sdougb	xmlTextWriterEndElement(writer); /* pools */
2135193149Sdougb	summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2136193149Sdougb
2137193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater");
2138193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2139193149Sdougb				       (isc_uint64_t)ctx->hi_water);
2140193149Sdougb	xmlTextWriterEndElement(writer); /* hiwater */
2141193149Sdougb
2142193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater");
2143193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2144193149Sdougb				       (isc_uint64_t)ctx->lo_water);
2145193149Sdougb	xmlTextWriterEndElement(writer); /* lowater */
2146193149Sdougb
2147193149Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
2148193149Sdougb
2149193149Sdougb	xmlTextWriterEndElement(writer); /* context */
2150193149Sdougb}
2151193149Sdougb
2152193149Sdougbvoid
2153193149Sdougbisc_mem_renderxml(xmlTextWriterPtr writer) {
2154193149Sdougb	isc_mem_t *ctx;
2155193149Sdougb	summarystat_t summary;
2156193149Sdougb	isc_uint64_t lost;
2157193149Sdougb
2158193149Sdougb	memset(&summary, 0, sizeof(summary));
2159193149Sdougb
2160193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts");
2161193149Sdougb
2162193149Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2163193149Sdougb
2164193149Sdougb	LOCK(&lock);
2165193149Sdougb	lost = totallost;
2166193149Sdougb	for (ctx = ISC_LIST_HEAD(contexts);
2167193149Sdougb	     ctx != NULL;
2168193149Sdougb	     ctx = ISC_LIST_NEXT(ctx, link)) {
2169193149Sdougb		renderctx(ctx, &summary, writer);
2170193149Sdougb	}
2171193149Sdougb	UNLOCK(&lock);
2172193149Sdougb
2173193149Sdougb	xmlTextWriterEndElement(writer); /* contexts */
2174193149Sdougb
2175193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary");
2176193149Sdougb
2177193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse");
2178193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2179193149Sdougb				       summary.total);
2180193149Sdougb	xmlTextWriterEndElement(writer); /* TotalUse */
2181193149Sdougb
2182193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse");
2183193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2184193149Sdougb				       summary.inuse);
2185193149Sdougb	xmlTextWriterEndElement(writer); /* InUse */
2186193149Sdougb
2187193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize");
2188193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2189193149Sdougb				       summary.blocksize);
2190193149Sdougb	xmlTextWriterEndElement(writer); /* BlockSize */
2191193149Sdougb
2192193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize");
2193193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2194193149Sdougb				       summary.contextsize);
2195193149Sdougb	xmlTextWriterEndElement(writer); /* ContextSize */
2196193149Sdougb
2197193149Sdougb	xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost");
2198193149Sdougb	xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2199193149Sdougb				       lost);
2200193149Sdougb	xmlTextWriterEndElement(writer); /* Lost */
2201193149Sdougb
2202193149Sdougb	xmlTextWriterEndElement(writer); /* summary */
2203193149Sdougb}
2204193149Sdougb
2205193149Sdougb#endif /* HAVE_LIBXML2 */
2206