1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2010, 2012-2014  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
18234010Sdougb/* $Id$ */
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 */
63224092Sdougbtypedef struct isc__mem isc__mem_t;
64224092Sdougbtypedef struct isc__mempool isc__mempool_t;
65224092Sdougb
66135446Strhodes#if ISC_MEM_TRACKLINES
67135446Strhodestypedef struct debuglink debuglink_t;
68135446Strhodesstruct debuglink {
69135446Strhodes	ISC_LINK(debuglink_t)	link;
70135446Strhodes	const void	       *ptr[DEBUGLIST_COUNT];
71262706Serwin	size_t			size[DEBUGLIST_COUNT];
72135446Strhodes	const char	       *file[DEBUGLIST_COUNT];
73135446Strhodes	unsigned int		line[DEBUGLIST_COUNT];
74135446Strhodes	unsigned int		count;
75135446Strhodes};
76135446Strhodes
77135446Strhodes#define FLARG_PASS	, file, line
78218384Sdougb#define FLARG		, const char *file, unsigned int line
79135446Strhodes#else
80135446Strhodes#define FLARG_PASS
81135446Strhodes#define FLARG
82135446Strhodes#endif
83135446Strhodes
84135446Strhodestypedef struct element element;
85135446Strhodesstruct element {
86135446Strhodes	element *		next;
87135446Strhodes};
88135446Strhodes
89135446Strhodestypedef struct {
90170222Sdougb	/*!
91135446Strhodes	 * This structure must be ALIGNMENT_SIZE bytes.
92135446Strhodes	 */
93135446Strhodes	union {
94135446Strhodes		size_t		size;
95224092Sdougb		isc__mem_t	*ctx;
96135446Strhodes		char		bytes[ALIGNMENT_SIZE];
97135446Strhodes	} u;
98135446Strhodes} size_info;
99135446Strhodes
100135446Strhodesstruct stats {
101135446Strhodes	unsigned long		gets;
102135446Strhodes	unsigned long		totalgets;
103135446Strhodes	unsigned long		blocks;
104135446Strhodes	unsigned long		freefrags;
105135446Strhodes};
106135446Strhodes
107135446Strhodes#define MEM_MAGIC		ISC_MAGIC('M', 'e', 'm', 'C')
108135446Strhodes#define VALID_CONTEXT(c)	ISC_MAGIC_VALID(c, MEM_MAGIC)
109135446Strhodes
110135446Strhodes#if ISC_MEM_TRACKLINES
111135446Strhodestypedef ISC_LIST(debuglink_t)	debuglist_t;
112135446Strhodes#endif
113135446Strhodes
114170222Sdougb/* List of all active memory contexts. */
115170222Sdougb
116224092Sdougbstatic ISC_LIST(isc__mem_t)	contexts;
117170222Sdougbstatic isc_once_t		once = ISC_ONCE_INIT;
118170222Sdougbstatic isc_mutex_t		lock;
119170222Sdougb
120193149Sdougb/*%
121193149Sdougb * Total size of lost memory due to a bug of external library.
122193149Sdougb * Locked by the global lock.
123193149Sdougb */
124193149Sdougbstatic isc_uint64_t		totallost;
125193149Sdougb
126224092Sdougbstruct isc__mem {
127224092Sdougb	isc_mem_t		common;
128135446Strhodes	isc_ondestroy_t		ondestroy;
129170222Sdougb	unsigned int		flags;
130135446Strhodes	isc_mutex_t		lock;
131135446Strhodes	isc_memalloc_t		memalloc;
132135446Strhodes	isc_memfree_t		memfree;
133135446Strhodes	void *			arg;
134135446Strhodes	size_t			max_size;
135135446Strhodes	isc_boolean_t		checkfree;
136135446Strhodes	struct stats *		stats;
137135446Strhodes	unsigned int		references;
138193149Sdougb	char			name[16];
139193149Sdougb	void *			tag;
140135446Strhodes	size_t			quota;
141135446Strhodes	size_t			total;
142135446Strhodes	size_t			inuse;
143135446Strhodes	size_t			maxinuse;
144135446Strhodes	size_t			hi_water;
145135446Strhodes	size_t			lo_water;
146135446Strhodes	isc_boolean_t		hi_called;
147214586Sdougb	isc_boolean_t		is_overmem;
148135446Strhodes	isc_mem_water_t		water;
149135446Strhodes	void *			water_arg;
150224092Sdougb	ISC_LIST(isc__mempool_t) pools;
151193149Sdougb	unsigned int		poolcnt;
152135446Strhodes
153170222Sdougb	/*  ISC_MEMFLAG_INTERNAL */
154135446Strhodes	size_t			mem_target;
155135446Strhodes	element **		freelists;
156135446Strhodes	element *		basic_blocks;
157135446Strhodes	unsigned char **	basic_table;
158135446Strhodes	unsigned int		basic_table_count;
159135446Strhodes	unsigned int		basic_table_size;
160135446Strhodes	unsigned char *		lowest;
161135446Strhodes	unsigned char *		highest;
162135446Strhodes
163135446Strhodes#if ISC_MEM_TRACKLINES
164135446Strhodes	debuglist_t *	 	debuglist;
165193149Sdougb	unsigned int		debuglistcnt;
166135446Strhodes#endif
167135446Strhodes
168135446Strhodes	unsigned int		memalloc_failures;
169224092Sdougb	ISC_LINK(isc__mem_t)	link;
170135446Strhodes};
171135446Strhodes
172135446Strhodes#define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
173135446Strhodes#define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
174135446Strhodes
175224092Sdougbstruct isc__mempool {
176135446Strhodes	/* always unlocked */
177224092Sdougb	isc_mempool_t	common;		/*%< common header of mempool's */
178170222Sdougb	isc_mutex_t    *lock;		/*%< optional lock */
179224092Sdougb	isc__mem_t      *mctx;		/*%< our memory context */
180170222Sdougb	/*%< locked via the memory context's lock */
181224092Sdougb	ISC_LINK(isc__mempool_t)	link;	/*%< next pool in this mem context */
182170222Sdougb	/*%< optionally locked from here down */
183170222Sdougb	element	       *items;		/*%< low water item list */
184170222Sdougb	size_t		size;		/*%< size of each item on this pool */
185170222Sdougb	unsigned int	maxalloc;	/*%< max number of items allowed */
186170222Sdougb	unsigned int	allocated;	/*%< # of items currently given out */
187170222Sdougb	unsigned int	freecount;	/*%< # of items on reserved list */
188170222Sdougb	unsigned int	freemax;	/*%< # of items allowed on free list */
189170222Sdougb	unsigned int	fillcount;	/*%< # of items to fetch on each fill */
190170222Sdougb	/*%< Stats only. */
191170222Sdougb	unsigned int	gets;		/*%< # of requests to this pool */
192170222Sdougb	/*%< Debugging only. */
193135446Strhodes#if ISC_MEMPOOL_NAMES
194170222Sdougb	char		name[16];	/*%< printed name in stats reports */
195135446Strhodes#endif
196135446Strhodes};
197135446Strhodes
198135446Strhodes/*
199135446Strhodes * Private Inline-able.
200135446Strhodes */
201135446Strhodes
202135446Strhodes#if ! ISC_MEM_TRACKLINES
203135446Strhodes#define ADD_TRACE(a, b, c, d, e)
204135446Strhodes#define DELETE_TRACE(a, b, c, d, e)
205254402Serwin#define ISC_MEMFUNC_SCOPE
206135446Strhodes#else
207135446Strhodes#define ADD_TRACE(a, b, c, d, e) \
208135446Strhodes	do { \
209135446Strhodes		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
210135446Strhodes					  ISC_MEM_DEBUGRECORD)) != 0 && \
211135446Strhodes		     b != NULL) \
212186462Sdougb			 add_trace_entry(a, b, c, d, e); \
213135446Strhodes	} while (0)
214135446Strhodes#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)
215135446Strhodes
216135446Strhodesstatic void
217224092Sdougbprint_active(isc__mem_t *ctx, FILE *out);
218135446Strhodes
219224092Sdougb/*%
220224092Sdougb * The following can be either static or public, depending on build environment.
221224092Sdougb */
222224092Sdougb
223224092Sdougb#ifdef BIND9
224224092Sdougb#define ISC_MEMFUNC_SCOPE
225224092Sdougb#else
226224092Sdougb#define ISC_MEMFUNC_SCOPE static
227224092Sdougb#endif
228224092Sdougb
229224092SdougbISC_MEMFUNC_SCOPE isc_result_t
230224092Sdougbisc__mem_createx(size_t init_max_size, size_t target_size,
231224092Sdougb		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
232224092Sdougb		 isc_mem_t **ctxp);
233224092SdougbISC_MEMFUNC_SCOPE isc_result_t
234224092Sdougbisc__mem_createx2(size_t init_max_size, size_t target_size,
235224092Sdougb		  isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
236224092Sdougb		  isc_mem_t **ctxp, unsigned int flags);
237224092SdougbISC_MEMFUNC_SCOPE isc_result_t
238224092Sdougbisc__mem_create(size_t init_max_size, size_t target_size, isc_mem_t **ctxp);
239224092SdougbISC_MEMFUNC_SCOPE isc_result_t
240224092Sdougbisc__mem_create2(size_t init_max_size, size_t target_size,
241224092Sdougb		 isc_mem_t **ctxp, unsigned int flags);
242224092SdougbISC_MEMFUNC_SCOPE void
243224092Sdougbisc__mem_attach(isc_mem_t *source, isc_mem_t **targetp);
244224092SdougbISC_MEMFUNC_SCOPE void
245224092Sdougbisc__mem_detach(isc_mem_t **ctxp);
246224092SdougbISC_MEMFUNC_SCOPE void
247224092Sdougbisc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG);
248224092SdougbISC_MEMFUNC_SCOPE void
249224092Sdougbisc__mem_destroy(isc_mem_t **ctxp);
250224092SdougbISC_MEMFUNC_SCOPE isc_result_t
251224092Sdougbisc__mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event);
252224092SdougbISC_MEMFUNC_SCOPE void *
253224092Sdougbisc___mem_get(isc_mem_t *ctx, size_t size FLARG);
254224092SdougbISC_MEMFUNC_SCOPE void
255224092Sdougbisc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG);
256224092SdougbISC_MEMFUNC_SCOPE void
257224092Sdougbisc__mem_stats(isc_mem_t *ctx, FILE *out);
258224092SdougbISC_MEMFUNC_SCOPE void *
259224092Sdougbisc___mem_allocate(isc_mem_t *ctx, size_t size FLARG);
260224092SdougbISC_MEMFUNC_SCOPE void *
261224092Sdougbisc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG);
262224092SdougbISC_MEMFUNC_SCOPE void
263224092Sdougbisc___mem_free(isc_mem_t *ctx, void *ptr FLARG);
264224092SdougbISC_MEMFUNC_SCOPE char *
265224092Sdougbisc___mem_strdup(isc_mem_t *mctx, const char *s FLARG);
266224092SdougbISC_MEMFUNC_SCOPE void
267224092Sdougbisc__mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag);
268224092SdougbISC_MEMFUNC_SCOPE void
269224092Sdougbisc__mem_setquota(isc_mem_t *ctx, size_t quota);
270224092SdougbISC_MEMFUNC_SCOPE size_t
271224092Sdougbisc__mem_getquota(isc_mem_t *ctx);
272224092SdougbISC_MEMFUNC_SCOPE size_t
273224092Sdougbisc__mem_inuse(isc_mem_t *ctx);
274224092SdougbISC_MEMFUNC_SCOPE isc_boolean_t
275224092Sdougbisc__mem_isovermem(isc_mem_t *ctx);
276224092SdougbISC_MEMFUNC_SCOPE void
277224092Sdougbisc__mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
278224092Sdougb		  size_t hiwater, size_t lowater);
279224092SdougbISC_MEMFUNC_SCOPE void
280224092Sdougbisc__mem_waterack(isc_mem_t *ctx0, int flag);
281224092SdougbISC_MEMFUNC_SCOPE void
282224092Sdougbisc__mem_setname(isc_mem_t *ctx, const char *name, void *tag);
283224092SdougbISC_MEMFUNC_SCOPE const char *
284224092Sdougbisc__mem_getname(isc_mem_t *ctx);
285224092SdougbISC_MEMFUNC_SCOPE void *
286224092Sdougbisc__mem_gettag(isc_mem_t *ctx);
287224092SdougbISC_MEMFUNC_SCOPE isc_result_t
288224092Sdougbisc__mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp);
289224092SdougbISC_MEMFUNC_SCOPE void
290224092Sdougbisc__mempool_setname(isc_mempool_t *mpctx, const char *name);
291224092SdougbISC_MEMFUNC_SCOPE void
292224092Sdougbisc__mempool_destroy(isc_mempool_t **mpctxp);
293224092SdougbISC_MEMFUNC_SCOPE void
294224092Sdougbisc__mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock);
295224092SdougbISC_MEMFUNC_SCOPE void *
296224092Sdougbisc___mempool_get(isc_mempool_t *mpctx FLARG);
297224092SdougbISC_MEMFUNC_SCOPE void
298224092Sdougbisc___mempool_put(isc_mempool_t *mpctx, void *mem FLARG);
299224092SdougbISC_MEMFUNC_SCOPE void
300224092Sdougbisc__mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit);
301224092SdougbISC_MEMFUNC_SCOPE unsigned int
302224092Sdougbisc__mempool_getfreemax(isc_mempool_t *mpctx);
303224092SdougbISC_MEMFUNC_SCOPE unsigned int
304224092Sdougbisc__mempool_getfreecount(isc_mempool_t *mpctx);
305224092SdougbISC_MEMFUNC_SCOPE void
306224092Sdougbisc__mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit);
307224092SdougbISC_MEMFUNC_SCOPE unsigned int
308224092Sdougbisc__mempool_getmaxalloc(isc_mempool_t *mpctx);
309224092SdougbISC_MEMFUNC_SCOPE unsigned int
310224092Sdougbisc__mempool_getallocated(isc_mempool_t *mpctx);
311224092SdougbISC_MEMFUNC_SCOPE void
312224092Sdougbisc__mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit);
313224092SdougbISC_MEMFUNC_SCOPE unsigned int
314224092Sdougbisc__mempool_getfillcount(isc_mempool_t *mpctx);
315224092Sdougb#ifdef BIND9
316224092SdougbISC_MEMFUNC_SCOPE void
317224092Sdougbisc__mem_printactive(isc_mem_t *ctx0, FILE *file);
318224092SdougbISC_MEMFUNC_SCOPE void
319224092Sdougbisc__mem_printallactive(FILE *file);
320224092SdougbISC_MEMFUNC_SCOPE void
321224092Sdougbisc__mem_checkdestroyed(FILE *file);
322224092SdougbISC_MEMFUNC_SCOPE unsigned int
323224092Sdougbisc__mem_references(isc_mem_t *ctx0);
324224092Sdougb#endif
325254402Serwin#endif /* ISC_MEM_TRACKLINES */
326224092Sdougb
327224092Sdougbstatic struct isc__memmethods {
328224092Sdougb	isc_memmethods_t methods;
329224092Sdougb
330224092Sdougb	/*%
331224092Sdougb	 * The following are defined just for avoiding unused static functions.
332224092Sdougb	 */
333224092Sdougb#ifndef BIND9
334224092Sdougb	void *createx, *create, *create2, *ondestroy, *stats,
335224092Sdougb		*setquota, *getquota, *setname, *getname, *gettag;
336224092Sdougb#endif
337224092Sdougb} memmethods = {
338224092Sdougb	{
339224092Sdougb		isc__mem_attach,
340224092Sdougb		isc__mem_detach,
341224092Sdougb		isc__mem_destroy,
342224092Sdougb		isc___mem_get,
343224092Sdougb		isc___mem_put,
344224092Sdougb		isc___mem_putanddetach,
345224092Sdougb		isc___mem_allocate,
346224092Sdougb		isc___mem_reallocate,
347224092Sdougb		isc___mem_strdup,
348224092Sdougb		isc___mem_free,
349224092Sdougb		isc__mem_setdestroycheck,
350224092Sdougb		isc__mem_setwater,
351224092Sdougb		isc__mem_waterack,
352224092Sdougb		isc__mem_inuse,
353224092Sdougb		isc__mem_isovermem,
354224092Sdougb		isc__mempool_create
355224092Sdougb	}
356224092Sdougb#ifndef BIND9
357224092Sdougb	,
358224092Sdougb	(void *)isc__mem_createx, (void *)isc__mem_create,
359224092Sdougb	(void *)isc__mem_create2, (void *)isc__mem_ondestroy,
360224092Sdougb	(void *)isc__mem_stats, (void *)isc__mem_setquota,
361224092Sdougb	(void *)isc__mem_getquota, (void *)isc__mem_setname,
362224092Sdougb	(void *)isc__mem_getname, (void *)isc__mem_gettag
363224092Sdougb#endif
364224092Sdougb};
365224092Sdougb
366224092Sdougbstatic struct isc__mempoolmethods {
367224092Sdougb	isc_mempoolmethods_t methods;
368224092Sdougb
369224092Sdougb	/*%
370224092Sdougb	 * The following are defined just for avoiding unused static functions.
371224092Sdougb	 */
372224092Sdougb#ifndef BIND9
373224092Sdougb	void *getfreemax, *getfreecount, *getmaxalloc, *getfillcount;
374224092Sdougb#endif
375224092Sdougb} mempoolmethods = {
376224092Sdougb	{
377224092Sdougb		isc__mempool_destroy,
378224092Sdougb		isc___mempool_get,
379224092Sdougb		isc___mempool_put,
380224092Sdougb		isc__mempool_getallocated,
381224092Sdougb		isc__mempool_setmaxalloc,
382224092Sdougb		isc__mempool_setfreemax,
383224092Sdougb		isc__mempool_setname,
384224092Sdougb		isc__mempool_associatelock,
385224092Sdougb		isc__mempool_setfillcount
386224092Sdougb	}
387224092Sdougb#ifndef BIND9
388224092Sdougb	,
389224092Sdougb	(void *)isc__mempool_getfreemax, (void *)isc__mempool_getfreecount,
390224092Sdougb	(void *)isc__mempool_getmaxalloc, (void *)isc__mempool_getfillcount
391224092Sdougb#endif
392224092Sdougb};
393224092Sdougb
394254402Serwin#if ISC_MEM_TRACKLINES
395170222Sdougb/*!
396135446Strhodes * mctx must be locked.
397135446Strhodes */
398135446Strhodesstatic inline void
399262706Serwinadd_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
400135446Strhodes	debuglink_t *dl;
401135446Strhodes	unsigned int i;
402262706Serwin	size_t mysize = size;
403135446Strhodes
404135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
405135446Strhodes		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
406135446Strhodes					       ISC_MSG_ADDTRACE,
407135446Strhodes					       "add %p size %u "
408135446Strhodes					       "file %s line %u mctx %p\n"),
409135446Strhodes			ptr, size, file, line, mctx);
410135446Strhodes
411135446Strhodes	if (mctx->debuglist == NULL)
412135446Strhodes		return;
413135446Strhodes
414218384Sdougb	if (mysize > mctx->max_size)
415218384Sdougb		mysize = mctx->max_size;
416135446Strhodes
417218384Sdougb	dl = ISC_LIST_HEAD(mctx->debuglist[mysize]);
418135446Strhodes	while (dl != NULL) {
419135446Strhodes		if (dl->count == DEBUGLIST_COUNT)
420135446Strhodes			goto next;
421135446Strhodes		for (i = 0; i < DEBUGLIST_COUNT; i++) {
422135446Strhodes			if (dl->ptr[i] == NULL) {
423135446Strhodes				dl->ptr[i] = ptr;
424135446Strhodes				dl->size[i] = size;
425135446Strhodes				dl->file[i] = file;
426135446Strhodes				dl->line[i] = line;
427135446Strhodes				dl->count++;
428135446Strhodes				return;
429135446Strhodes			}
430135446Strhodes		}
431135446Strhodes	next:
432135446Strhodes		dl = ISC_LIST_NEXT(dl, link);
433135446Strhodes	}
434135446Strhodes
435135446Strhodes	dl = malloc(sizeof(debuglink_t));
436135446Strhodes	INSIST(dl != NULL);
437135446Strhodes
438135446Strhodes	ISC_LINK_INIT(dl, link);
439135446Strhodes	for (i = 1; i < DEBUGLIST_COUNT; i++) {
440135446Strhodes		dl->ptr[i] = NULL;
441135446Strhodes		dl->size[i] = 0;
442135446Strhodes		dl->file[i] = NULL;
443135446Strhodes		dl->line[i] = 0;
444135446Strhodes	}
445135446Strhodes
446135446Strhodes	dl->ptr[0] = ptr;
447135446Strhodes	dl->size[0] = size;
448135446Strhodes	dl->file[0] = file;
449135446Strhodes	dl->line[0] = line;
450135446Strhodes	dl->count = 1;
451135446Strhodes
452218384Sdougb	ISC_LIST_PREPEND(mctx->debuglist[mysize], dl, link);
453193149Sdougb	mctx->debuglistcnt++;
454135446Strhodes}
455135446Strhodes
456135446Strhodesstatic inline void
457262706Serwindelete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
458135446Strhodes		   const char *file, unsigned int line)
459135446Strhodes{
460135446Strhodes	debuglink_t *dl;
461135446Strhodes	unsigned int i;
462135446Strhodes
463135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
464135446Strhodes		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
465135446Strhodes					       ISC_MSG_DELTRACE,
466135446Strhodes					       "del %p size %u "
467135446Strhodes					       "file %s line %u mctx %p\n"),
468135446Strhodes			ptr, size, file, line, mctx);
469135446Strhodes
470135446Strhodes	if (mctx->debuglist == NULL)
471135446Strhodes		return;
472135446Strhodes
473135446Strhodes	if (size > mctx->max_size)
474135446Strhodes		size = mctx->max_size;
475135446Strhodes
476135446Strhodes	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
477135446Strhodes	while (dl != NULL) {
478135446Strhodes		for (i = 0; i < DEBUGLIST_COUNT; i++) {
479135446Strhodes			if (dl->ptr[i] == ptr) {
480135446Strhodes				dl->ptr[i] = NULL;
481135446Strhodes				dl->size[i] = 0;
482135446Strhodes				dl->file[i] = NULL;
483135446Strhodes				dl->line[i] = 0;
484135446Strhodes
485135446Strhodes				INSIST(dl->count > 0);
486135446Strhodes				dl->count--;
487135446Strhodes				if (dl->count == 0) {
488135446Strhodes					ISC_LIST_UNLINK(mctx->debuglist[size],
489135446Strhodes							dl, link);
490135446Strhodes					free(dl);
491135446Strhodes				}
492135446Strhodes				return;
493135446Strhodes			}
494135446Strhodes		}
495135446Strhodes		dl = ISC_LIST_NEXT(dl, link);
496135446Strhodes	}
497135446Strhodes
498135446Strhodes	/*
499135446Strhodes	 * If we get here, we didn't find the item on the list.  We're
500135446Strhodes	 * screwed.
501135446Strhodes	 */
502135446Strhodes	INSIST(dl != NULL);
503135446Strhodes}
504135446Strhodes#endif /* ISC_MEM_TRACKLINES */
505135446Strhodes
506135446Strhodesstatic inline size_t
507135446Strhodesrmsize(size_t size) {
508135446Strhodes	/*
509186462Sdougb	 * round down to ALIGNMENT_SIZE
510135446Strhodes	 */
511135446Strhodes	return (size & (~(ALIGNMENT_SIZE - 1)));
512135446Strhodes}
513135446Strhodes
514135446Strhodesstatic inline size_t
515135446Strhodesquantize(size_t size) {
516170222Sdougb	/*!
517135446Strhodes	 * Round up the result in order to get a size big
518135446Strhodes	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
519135446Strhodes	 * byte boundaries.
520135446Strhodes	 */
521135446Strhodes
522170222Sdougb	if (size == 0U)
523135446Strhodes		return (ALIGNMENT_SIZE);
524135446Strhodes	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
525135446Strhodes}
526135446Strhodes
527135446Strhodesstatic inline isc_boolean_t
528224092Sdougbmore_basic_blocks(isc__mem_t *ctx) {
529135446Strhodes	void *new;
530135446Strhodes	unsigned char *curr, *next;
531135446Strhodes	unsigned char *first, *last;
532135446Strhodes	unsigned char **table;
533135446Strhodes	unsigned int table_size;
534135446Strhodes	size_t increment;
535135446Strhodes	int i;
536135446Strhodes
537135446Strhodes	/* Require: we hold the context lock. */
538135446Strhodes
539135446Strhodes	/*
540135446Strhodes	 * Did we hit the quota for this context?
541135446Strhodes	 */
542135446Strhodes	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
543170222Sdougb	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
544135446Strhodes		return (ISC_FALSE);
545135446Strhodes
546135446Strhodes	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
547135446Strhodes	if (ctx->basic_table_count == ctx->basic_table_size) {
548135446Strhodes		table_size = ctx->basic_table_size + TABLE_INCREMENT;
549135446Strhodes		table = (ctx->memalloc)(ctx->arg,
550135446Strhodes					table_size * sizeof(unsigned char *));
551135446Strhodes		if (table == NULL) {
552135446Strhodes			ctx->memalloc_failures++;
553135446Strhodes			return (ISC_FALSE);
554135446Strhodes		}
555135446Strhodes		if (ctx->basic_table_size != 0) {
556262706Serwin			memmove(table, ctx->basic_table,
557262706Serwin				ctx->basic_table_size *
558262706Serwin				  sizeof(unsigned char *));
559135446Strhodes			(ctx->memfree)(ctx->arg, ctx->basic_table);
560135446Strhodes		}
561135446Strhodes		ctx->basic_table = table;
562135446Strhodes		ctx->basic_table_size = table_size;
563135446Strhodes	}
564135446Strhodes
565135446Strhodes	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
566135446Strhodes	if (new == NULL) {
567135446Strhodes		ctx->memalloc_failures++;
568135446Strhodes		return (ISC_FALSE);
569135446Strhodes	}
570135446Strhodes	ctx->total += increment;
571135446Strhodes	ctx->basic_table[ctx->basic_table_count] = new;
572135446Strhodes	ctx->basic_table_count++;
573135446Strhodes
574135446Strhodes	curr = new;
575135446Strhodes	next = curr + ctx->mem_target;
576135446Strhodes	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
577135446Strhodes		((element *)curr)->next = (element *)next;
578135446Strhodes		curr = next;
579135446Strhodes		next += ctx->mem_target;
580135446Strhodes	}
581135446Strhodes	/*
582135446Strhodes	 * curr is now pointing at the last block in the
583135446Strhodes	 * array.
584135446Strhodes	 */
585135446Strhodes	((element *)curr)->next = NULL;
586135446Strhodes	first = new;
587135446Strhodes	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
588135446Strhodes	if (first < ctx->lowest || ctx->lowest == NULL)
589135446Strhodes		ctx->lowest = first;
590135446Strhodes	if (last > ctx->highest)
591135446Strhodes		ctx->highest = last;
592135446Strhodes	ctx->basic_blocks = new;
593135446Strhodes
594135446Strhodes	return (ISC_TRUE);
595135446Strhodes}
596135446Strhodes
597135446Strhodesstatic inline isc_boolean_t
598224092Sdougbmore_frags(isc__mem_t *ctx, size_t new_size) {
599135446Strhodes	int i, frags;
600135446Strhodes	size_t total_size;
601135446Strhodes	void *new;
602135446Strhodes	unsigned char *curr, *next;
603135446Strhodes
604170222Sdougb	/*!
605135446Strhodes	 * Try to get more fragments by chopping up a basic block.
606135446Strhodes	 */
607135446Strhodes
608135446Strhodes	if (ctx->basic_blocks == NULL) {
609135446Strhodes		if (!more_basic_blocks(ctx)) {
610135446Strhodes			/*
611135446Strhodes			 * We can't get more memory from the OS, or we've
612135446Strhodes			 * hit the quota for this context.
613135446Strhodes			 */
614135446Strhodes			/*
615135446Strhodes			 * XXXRTH  "At quota" notification here.
616135446Strhodes			 */
617135446Strhodes			return (ISC_FALSE);
618135446Strhodes		}
619135446Strhodes	}
620135446Strhodes
621135446Strhodes	total_size = ctx->mem_target;
622135446Strhodes	new = ctx->basic_blocks;
623135446Strhodes	ctx->basic_blocks = ctx->basic_blocks->next;
624262706Serwin	frags = (int)(total_size / new_size);
625135446Strhodes	ctx->stats[new_size].blocks++;
626135446Strhodes	ctx->stats[new_size].freefrags += frags;
627135446Strhodes	/*
628135446Strhodes	 * Set up a linked-list of blocks of size
629135446Strhodes	 * "new_size".
630135446Strhodes	 */
631135446Strhodes	curr = new;
632135446Strhodes	next = curr + new_size;
633135446Strhodes	total_size -= new_size;
634135446Strhodes	for (i = 0; i < (frags - 1); i++) {
635135446Strhodes		((element *)curr)->next = (element *)next;
636135446Strhodes		curr = next;
637135446Strhodes		next += new_size;
638135446Strhodes		total_size -= new_size;
639135446Strhodes	}
640135446Strhodes	/*
641135446Strhodes	 * Add the remaining fragment of the basic block to a free list.
642135446Strhodes	 */
643135446Strhodes	total_size = rmsize(total_size);
644170222Sdougb	if (total_size > 0U) {
645135446Strhodes		((element *)next)->next = ctx->freelists[total_size];
646135446Strhodes		ctx->freelists[total_size] = (element *)next;
647135446Strhodes		ctx->stats[total_size].freefrags++;
648135446Strhodes	}
649135446Strhodes	/*
650135446Strhodes	 * curr is now pointing at the last block in the
651135446Strhodes	 * array.
652135446Strhodes	 */
653135446Strhodes	((element *)curr)->next = NULL;
654135446Strhodes	ctx->freelists[new_size] = new;
655135446Strhodes
656135446Strhodes	return (ISC_TRUE);
657135446Strhodes}
658135446Strhodes
659135446Strhodesstatic inline void *
660224092Sdougbmem_getunlocked(isc__mem_t *ctx, size_t size) {
661135446Strhodes	size_t new_size = quantize(size);
662135446Strhodes	void *ret;
663135446Strhodes
664135446Strhodes	if (size >= ctx->max_size || new_size >= ctx->max_size) {
665135446Strhodes		/*
666135446Strhodes		 * memget() was called on something beyond our upper limit.
667135446Strhodes		 */
668170222Sdougb		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
669135446Strhodes			ret = NULL;
670135446Strhodes			goto done;
671135446Strhodes		}
672135446Strhodes		ret = (ctx->memalloc)(ctx->arg, size);
673135446Strhodes		if (ret == NULL) {
674135446Strhodes			ctx->memalloc_failures++;
675135446Strhodes			goto done;
676135446Strhodes		}
677135446Strhodes		ctx->total += size;
678135446Strhodes		ctx->inuse += size;
679135446Strhodes		ctx->stats[ctx->max_size].gets++;
680135446Strhodes		ctx->stats[ctx->max_size].totalgets++;
681135446Strhodes		/*
682135446Strhodes		 * If we don't set new_size to size, then the
683135446Strhodes		 * ISC_MEM_FILL code might write over bytes we
684135446Strhodes		 * don't own.
685135446Strhodes		 */
686135446Strhodes		new_size = size;
687135446Strhodes		goto done;
688135446Strhodes	}
689135446Strhodes
690135446Strhodes	/*
691135446Strhodes	 * If there are no blocks in the free list for this size, get a chunk
692135446Strhodes	 * of memory and then break it up into "new_size"-sized blocks, adding
693135446Strhodes	 * them to the free list.
694135446Strhodes	 */
695135446Strhodes	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
696135446Strhodes		return (NULL);
697135446Strhodes
698135446Strhodes	/*
699135446Strhodes	 * The free list uses the "rounded-up" size "new_size".
700135446Strhodes	 */
701135446Strhodes	ret = ctx->freelists[new_size];
702135446Strhodes	ctx->freelists[new_size] = ctx->freelists[new_size]->next;
703135446Strhodes
704135446Strhodes	/*
705135446Strhodes	 * The stats[] uses the _actual_ "size" requested by the
706135446Strhodes	 * caller, with the caveat (in the code above) that "size" >= the
707135446Strhodes	 * max. size (max_size) ends up getting recorded as a call to
708135446Strhodes	 * max_size.
709135446Strhodes	 */
710135446Strhodes	ctx->stats[size].gets++;
711135446Strhodes	ctx->stats[size].totalgets++;
712135446Strhodes	ctx->stats[new_size].freefrags--;
713135446Strhodes	ctx->inuse += new_size;
714135446Strhodes
715135446Strhodes done:
716135446Strhodes
717135446Strhodes#if ISC_MEM_FILL
718135446Strhodes	if (ret != NULL)
719135446Strhodes		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
720135446Strhodes#endif
721135446Strhodes
722135446Strhodes	return (ret);
723135446Strhodes}
724135446Strhodes
725135446Strhodes#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
726135446Strhodesstatic inline void
727135446Strhodescheck_overrun(void *mem, size_t size, size_t new_size) {
728135446Strhodes	unsigned char *cp;
729135446Strhodes
730135446Strhodes	cp = (unsigned char *)mem;
731135446Strhodes	cp += size;
732135446Strhodes	while (size < new_size) {
733135446Strhodes		INSIST(*cp == 0xbe);
734135446Strhodes		cp++;
735135446Strhodes		size++;
736135446Strhodes	}
737135446Strhodes}
738135446Strhodes#endif
739135446Strhodes
740135446Strhodesstatic inline void
741224092Sdougbmem_putunlocked(isc__mem_t *ctx, void *mem, size_t size) {
742135446Strhodes	size_t new_size = quantize(size);
743135446Strhodes
744135446Strhodes	if (size == ctx->max_size || new_size >= ctx->max_size) {
745135446Strhodes		/*
746135446Strhodes		 * memput() called on something beyond our upper limit.
747135446Strhodes		 */
748135446Strhodes#if ISC_MEM_FILL
749135446Strhodes		memset(mem, 0xde, size); /* Mnemonic for "dead". */
750135446Strhodes#endif
751135446Strhodes		(ctx->memfree)(ctx->arg, mem);
752170222Sdougb		INSIST(ctx->stats[ctx->max_size].gets != 0U);
753135446Strhodes		ctx->stats[ctx->max_size].gets--;
754135446Strhodes		INSIST(size <= ctx->total);
755135446Strhodes		ctx->inuse -= size;
756135446Strhodes		ctx->total -= size;
757135446Strhodes		return;
758135446Strhodes	}
759135446Strhodes
760135446Strhodes#if ISC_MEM_FILL
761135446Strhodes#if ISC_MEM_CHECKOVERRUN
762135446Strhodes	check_overrun(mem, size, new_size);
763135446Strhodes#endif
764135446Strhodes	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
765135446Strhodes#endif
766135446Strhodes
767135446Strhodes	/*
768135446Strhodes	 * The free list uses the "rounded-up" size "new_size".
769135446Strhodes	 */
770135446Strhodes	((element *)mem)->next = ctx->freelists[new_size];
771135446Strhodes	ctx->freelists[new_size] = (element *)mem;
772135446Strhodes
773135446Strhodes	/*
774135446Strhodes	 * The stats[] uses the _actual_ "size" requested by the
775135446Strhodes	 * caller, with the caveat (in the code above) that "size" >= the
776135446Strhodes	 * max. size (max_size) ends up getting recorded as a call to
777135446Strhodes	 * max_size.
778135446Strhodes	 */
779170222Sdougb	INSIST(ctx->stats[size].gets != 0U);
780135446Strhodes	ctx->stats[size].gets--;
781135446Strhodes	ctx->stats[new_size].freefrags++;
782135446Strhodes	ctx->inuse -= new_size;
783135446Strhodes}
784135446Strhodes
785170222Sdougb/*!
786135446Strhodes * Perform a malloc, doing memory filling and overrun detection as necessary.
787135446Strhodes */
788135446Strhodesstatic inline void *
789224092Sdougbmem_get(isc__mem_t *ctx, size_t size) {
790135446Strhodes	char *ret;
791135446Strhodes
792135446Strhodes#if ISC_MEM_CHECKOVERRUN
793135446Strhodes	size += 1;
794135446Strhodes#endif
795135446Strhodes
796135446Strhodes	ret = (ctx->memalloc)(ctx->arg, size);
797135446Strhodes	if (ret == NULL)
798186462Sdougb		ctx->memalloc_failures++;
799135446Strhodes
800135446Strhodes#if ISC_MEM_FILL
801135446Strhodes	if (ret != NULL)
802135446Strhodes		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
803135446Strhodes#else
804135446Strhodes#  if ISC_MEM_CHECKOVERRUN
805135446Strhodes	if (ret != NULL)
806135446Strhodes		ret[size-1] = 0xbe;
807135446Strhodes#  endif
808135446Strhodes#endif
809135446Strhodes
810135446Strhodes	return (ret);
811135446Strhodes}
812135446Strhodes
813170222Sdougb/*!
814135446Strhodes * Perform a free, doing memory filling and overrun detection as necessary.
815135446Strhodes */
816135446Strhodesstatic inline void
817224092Sdougbmem_put(isc__mem_t *ctx, void *mem, size_t size) {
818135446Strhodes#if ISC_MEM_CHECKOVERRUN
819135446Strhodes	INSIST(((unsigned char *)mem)[size] == 0xbe);
820135446Strhodes#endif
821135446Strhodes#if ISC_MEM_FILL
822135446Strhodes	memset(mem, 0xde, size); /* Mnemonic for "dead". */
823135446Strhodes#else
824135446Strhodes	UNUSED(size);
825135446Strhodes#endif
826135446Strhodes	(ctx->memfree)(ctx->arg, mem);
827135446Strhodes}
828135446Strhodes
829170222Sdougb/*!
830135446Strhodes * Update internal counters after a memory get.
831135446Strhodes */
832135446Strhodesstatic inline void
833224092Sdougbmem_getstats(isc__mem_t *ctx, size_t size) {
834135446Strhodes	ctx->total += size;
835135446Strhodes	ctx->inuse += size;
836135446Strhodes
837135446Strhodes	if (size > ctx->max_size) {
838135446Strhodes		ctx->stats[ctx->max_size].gets++;
839135446Strhodes		ctx->stats[ctx->max_size].totalgets++;
840135446Strhodes	} else {
841135446Strhodes		ctx->stats[size].gets++;
842135446Strhodes		ctx->stats[size].totalgets++;
843135446Strhodes	}
844135446Strhodes}
845135446Strhodes
846170222Sdougb/*!
847135446Strhodes * Update internal counters after a memory put.
848135446Strhodes */
849135446Strhodesstatic inline void
850224092Sdougbmem_putstats(isc__mem_t *ctx, void *ptr, size_t size) {
851135446Strhodes	UNUSED(ptr);
852135446Strhodes
853135446Strhodes	INSIST(ctx->inuse >= size);
854135446Strhodes	ctx->inuse -= size;
855135446Strhodes
856135446Strhodes	if (size > ctx->max_size) {
857135446Strhodes		INSIST(ctx->stats[ctx->max_size].gets > 0U);
858135446Strhodes		ctx->stats[ctx->max_size].gets--;
859135446Strhodes	} else {
860135446Strhodes		INSIST(ctx->stats[size].gets > 0U);
861135446Strhodes		ctx->stats[size].gets--;
862135446Strhodes	}
863135446Strhodes}
864135446Strhodes
865135446Strhodes/*
866135446Strhodes * Private.
867135446Strhodes */
868135446Strhodes
869135446Strhodesstatic void *
870135446Strhodesdefault_memalloc(void *arg, size_t size) {
871135446Strhodes	UNUSED(arg);
872135446Strhodes	if (size == 0U)
873135446Strhodes		size = 1;
874135446Strhodes	return (malloc(size));
875135446Strhodes}
876135446Strhodes
877135446Strhodesstatic void
878135446Strhodesdefault_memfree(void *arg, void *ptr) {
879135446Strhodes	UNUSED(arg);
880135446Strhodes	free(ptr);
881135446Strhodes}
882135446Strhodes
883170222Sdougbstatic void
884170222Sdougbinitialize_action(void) {
885174187Sdougb	RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
886174187Sdougb	ISC_LIST_INIT(contexts);
887193149Sdougb	totallost = 0;
888170222Sdougb}
889170222Sdougb
890135446Strhodes/*
891135446Strhodes * Public.
892135446Strhodes */
893135446Strhodes
894224092SdougbISC_MEMFUNC_SCOPE isc_result_t
895224092Sdougbisc__mem_createx(size_t init_max_size, size_t target_size,
896224092Sdougb		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
897224092Sdougb		 isc_mem_t **ctxp)
898135446Strhodes{
899224092Sdougb	return (isc__mem_createx2(init_max_size, target_size, memalloc, memfree,
900224092Sdougb				  arg, ctxp, ISC_MEMFLAG_DEFAULT));
901186462Sdougb
902170222Sdougb}
903170222Sdougb
904224092SdougbISC_MEMFUNC_SCOPE isc_result_t
905224092Sdougbisc__mem_createx2(size_t init_max_size, size_t target_size,
906224092Sdougb		  isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
907224092Sdougb		  isc_mem_t **ctxp, unsigned int flags)
908170222Sdougb{
909224092Sdougb	isc__mem_t *ctx;
910135446Strhodes	isc_result_t result;
911135446Strhodes
912135446Strhodes	REQUIRE(ctxp != NULL && *ctxp == NULL);
913135446Strhodes	REQUIRE(memalloc != NULL);
914135446Strhodes	REQUIRE(memfree != NULL);
915135446Strhodes
916135446Strhodes	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);
917135446Strhodes
918170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
919135446Strhodes
920135446Strhodes	ctx = (memalloc)(arg, sizeof(*ctx));
921135446Strhodes	if (ctx == NULL)
922135446Strhodes		return (ISC_R_NOMEMORY);
923135446Strhodes
924170222Sdougb	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
925170222Sdougb		result = isc_mutex_init(&ctx->lock);
926170222Sdougb		if (result != ISC_R_SUCCESS) {
927170222Sdougb			(memfree)(arg, ctx);
928170222Sdougb			return (result);
929170222Sdougb		}
930153816Sdougb	}
931153816Sdougb
932135446Strhodes	if (init_max_size == 0U)
933135446Strhodes		ctx->max_size = DEF_MAX_SIZE;
934135446Strhodes	else
935135446Strhodes		ctx->max_size = init_max_size;
936170222Sdougb	ctx->flags = flags;
937135446Strhodes	ctx->references = 1;
938193149Sdougb	memset(ctx->name, 0, sizeof(ctx->name));
939193149Sdougb	ctx->tag = NULL;
940135446Strhodes	ctx->quota = 0;
941135446Strhodes	ctx->total = 0;
942135446Strhodes	ctx->inuse = 0;
943135446Strhodes	ctx->maxinuse = 0;
944135446Strhodes	ctx->hi_water = 0;
945135446Strhodes	ctx->lo_water = 0;
946135446Strhodes	ctx->hi_called = ISC_FALSE;
947214586Sdougb	ctx->is_overmem = ISC_FALSE;
948135446Strhodes	ctx->water = NULL;
949135446Strhodes	ctx->water_arg = NULL;
950224092Sdougb	ctx->common.impmagic = MEM_MAGIC;
951224092Sdougb	ctx->common.magic = ISCAPI_MCTX_MAGIC;
952224092Sdougb	ctx->common.methods = (isc_memmethods_t *)&memmethods;
953135446Strhodes	isc_ondestroy_init(&ctx->ondestroy);
954135446Strhodes	ctx->memalloc = memalloc;
955135446Strhodes	ctx->memfree = memfree;
956135446Strhodes	ctx->arg = arg;
957135446Strhodes	ctx->stats = NULL;
958135446Strhodes	ctx->checkfree = ISC_TRUE;
959135446Strhodes#if ISC_MEM_TRACKLINES
960135446Strhodes	ctx->debuglist = NULL;
961193149Sdougb	ctx->debuglistcnt = 0;
962135446Strhodes#endif
963135446Strhodes	ISC_LIST_INIT(ctx->pools);
964193149Sdougb	ctx->poolcnt = 0;
965135446Strhodes	ctx->freelists = NULL;
966170222Sdougb	ctx->basic_blocks = NULL;
967170222Sdougb	ctx->basic_table = NULL;
968170222Sdougb	ctx->basic_table_count = 0;
969170222Sdougb	ctx->basic_table_size = 0;
970170222Sdougb	ctx->lowest = NULL;
971170222Sdougb	ctx->highest = NULL;
972135446Strhodes
973135446Strhodes	ctx->stats = (memalloc)(arg,
974135446Strhodes				(ctx->max_size+1) * sizeof(struct stats));
975135446Strhodes	if (ctx->stats == NULL) {
976135446Strhodes		result = ISC_R_NOMEMORY;
977135446Strhodes		goto error;
978135446Strhodes	}
979135446Strhodes	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
980135446Strhodes
981170222Sdougb	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
982170222Sdougb		if (target_size == 0U)
983170222Sdougb			ctx->mem_target = DEF_MEM_TARGET;
984170222Sdougb		else
985170222Sdougb			ctx->mem_target = target_size;
986170222Sdougb		ctx->freelists = (memalloc)(arg, ctx->max_size *
987170222Sdougb						 sizeof(element *));
988170222Sdougb		if (ctx->freelists == NULL) {
989170222Sdougb			result = ISC_R_NOMEMORY;
990170222Sdougb			goto error;
991170222Sdougb		}
992170222Sdougb		memset(ctx->freelists, 0,
993170222Sdougb		       ctx->max_size * sizeof(element *));
994135446Strhodes	}
995135446Strhodes
996135446Strhodes#if ISC_MEM_TRACKLINES
997135446Strhodes	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
998135446Strhodes		unsigned int i;
999135446Strhodes
1000135446Strhodes		ctx->debuglist = (memalloc)(arg,
1001135446Strhodes				      (ctx->max_size+1) * sizeof(debuglist_t));
1002135446Strhodes		if (ctx->debuglist == NULL) {
1003135446Strhodes			result = ISC_R_NOMEMORY;
1004135446Strhodes			goto error;
1005135446Strhodes		}
1006135446Strhodes		for (i = 0; i <= ctx->max_size; i++)
1007135446Strhodes			ISC_LIST_INIT(ctx->debuglist[i]);
1008135446Strhodes	}
1009135446Strhodes#endif
1010135446Strhodes
1011135446Strhodes	ctx->memalloc_failures = 0;
1012135446Strhodes
1013170222Sdougb	LOCK(&lock);
1014170222Sdougb	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
1015170222Sdougb	UNLOCK(&lock);
1016170222Sdougb
1017224092Sdougb	*ctxp = (isc_mem_t *)ctx;
1018135446Strhodes	return (ISC_R_SUCCESS);
1019135446Strhodes
1020135446Strhodes  error:
1021153816Sdougb	if (ctx != NULL) {
1022153816Sdougb		if (ctx->stats != NULL)
1023135446Strhodes			(memfree)(arg, ctx->stats);
1024153816Sdougb		if (ctx->freelists != NULL)
1025135446Strhodes			(memfree)(arg, ctx->freelists);
1026135446Strhodes#if ISC_MEM_TRACKLINES
1027153816Sdougb		if (ctx->debuglist != NULL)
1028135446Strhodes			(ctx->memfree)(ctx->arg, ctx->debuglist);
1029135446Strhodes#endif /* ISC_MEM_TRACKLINES */
1030170222Sdougb		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
1031170222Sdougb			DESTROYLOCK(&ctx->lock);
1032135446Strhodes		(memfree)(arg, ctx);
1033135446Strhodes	}
1034135446Strhodes
1035135446Strhodes	return (result);
1036135446Strhodes}
1037135446Strhodes
1038224092SdougbISC_MEMFUNC_SCOPE isc_result_t
1039224092Sdougbisc__mem_create(size_t init_max_size, size_t target_size, isc_mem_t **ctxp) {
1040224092Sdougb	return (isc__mem_createx2(init_max_size, target_size,
1041224092Sdougb				  default_memalloc, default_memfree, NULL,
1042224092Sdougb				  ctxp, ISC_MEMFLAG_DEFAULT));
1043135446Strhodes}
1044135446Strhodes
1045224092SdougbISC_MEMFUNC_SCOPE isc_result_t
1046224092Sdougbisc__mem_create2(size_t init_max_size, size_t target_size,
1047224092Sdougb		 isc_mem_t **ctxp, unsigned int flags)
1048170222Sdougb{
1049224092Sdougb	return (isc__mem_createx2(init_max_size, target_size,
1050224092Sdougb				  default_memalloc, default_memfree, NULL,
1051224092Sdougb				  ctxp, flags));
1052170222Sdougb}
1053170222Sdougb
1054135446Strhodesstatic void
1055224092Sdougbdestroy(isc__mem_t *ctx) {
1056135446Strhodes	unsigned int i;
1057135446Strhodes	isc_ondestroy_t ondest;
1058135446Strhodes
1059170222Sdougb	LOCK(&lock);
1060170222Sdougb	ISC_LIST_UNLINK(contexts, ctx, link);
1061193149Sdougb	totallost += ctx->inuse;
1062170222Sdougb	UNLOCK(&lock);
1063170222Sdougb
1064224092Sdougb	ctx->common.impmagic = 0;
1065224092Sdougb	ctx->common.magic = 0;
1066218384Sdougb
1067135446Strhodes	INSIST(ISC_LIST_EMPTY(ctx->pools));
1068135446Strhodes
1069135446Strhodes#if ISC_MEM_TRACKLINES
1070135446Strhodes	if (ctx->debuglist != NULL) {
1071135446Strhodes		if (ctx->checkfree) {
1072135446Strhodes			for (i = 0; i <= ctx->max_size; i++) {
1073135446Strhodes				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
1074135446Strhodes					print_active(ctx, stderr);
1075135446Strhodes				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
1076135446Strhodes			}
1077135446Strhodes		} else {
1078135446Strhodes			debuglink_t *dl;
1079135446Strhodes
1080135446Strhodes			for (i = 0; i <= ctx->max_size; i++)
1081135446Strhodes				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
1082135446Strhodes				     dl != NULL;
1083135446Strhodes				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
1084135446Strhodes					ISC_LIST_UNLINK(ctx->debuglist[i],
1085186462Sdougb							dl, link);
1086135446Strhodes					free(dl);
1087135446Strhodes				}
1088135446Strhodes		}
1089135446Strhodes		(ctx->memfree)(ctx->arg, ctx->debuglist);
1090135446Strhodes	}
1091135446Strhodes#endif
1092135446Strhodes	INSIST(ctx->references == 0);
1093135446Strhodes
1094135446Strhodes	if (ctx->checkfree) {
1095135446Strhodes		for (i = 0; i <= ctx->max_size; i++) {
1096135446Strhodes#if ISC_MEM_TRACKLINES
1097135446Strhodes			if (ctx->stats[i].gets != 0U)
1098135446Strhodes				print_active(ctx, stderr);
1099135446Strhodes#endif
1100135446Strhodes			INSIST(ctx->stats[i].gets == 0U);
1101135446Strhodes		}
1102135446Strhodes	}
1103135446Strhodes
1104135446Strhodes	(ctx->memfree)(ctx->arg, ctx->stats);
1105135446Strhodes
1106170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1107170222Sdougb		for (i = 0; i < ctx->basic_table_count; i++)
1108170222Sdougb			(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
1109170222Sdougb		(ctx->memfree)(ctx->arg, ctx->freelists);
1110186462Sdougb		if (ctx->basic_table != NULL)
1111186462Sdougb			(ctx->memfree)(ctx->arg, ctx->basic_table);
1112170222Sdougb	}
1113135446Strhodes
1114135446Strhodes	ondest = ctx->ondestroy;
1115135446Strhodes
1116170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
1117170222Sdougb		DESTROYLOCK(&ctx->lock);
1118135446Strhodes	(ctx->memfree)(ctx->arg, ctx);
1119135446Strhodes
1120135446Strhodes	isc_ondestroy_notify(&ondest, ctx);
1121135446Strhodes}
1122135446Strhodes
1123224092SdougbISC_MEMFUNC_SCOPE void
1124224092Sdougbisc__mem_attach(isc_mem_t *source0, isc_mem_t **targetp) {
1125224092Sdougb	isc__mem_t *source = (isc__mem_t *)source0;
1126224092Sdougb
1127135446Strhodes	REQUIRE(VALID_CONTEXT(source));
1128135446Strhodes	REQUIRE(targetp != NULL && *targetp == NULL);
1129135446Strhodes
1130170222Sdougb	MCTXLOCK(source, &source->lock);
1131135446Strhodes	source->references++;
1132170222Sdougb	MCTXUNLOCK(source, &source->lock);
1133135446Strhodes
1134224092Sdougb	*targetp = (isc_mem_t *)source;
1135135446Strhodes}
1136135446Strhodes
1137224092SdougbISC_MEMFUNC_SCOPE void
1138224092Sdougbisc__mem_detach(isc_mem_t **ctxp) {
1139224092Sdougb	isc__mem_t *ctx;
1140135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
1141135446Strhodes
1142135446Strhodes	REQUIRE(ctxp != NULL);
1143224092Sdougb	ctx = (isc__mem_t *)*ctxp;
1144135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1145135446Strhodes
1146170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1147135446Strhodes	INSIST(ctx->references > 0);
1148135446Strhodes	ctx->references--;
1149135446Strhodes	if (ctx->references == 0)
1150135446Strhodes		want_destroy = ISC_TRUE;
1151170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1152135446Strhodes
1153135446Strhodes	if (want_destroy)
1154135446Strhodes		destroy(ctx);
1155135446Strhodes
1156135446Strhodes	*ctxp = NULL;
1157135446Strhodes}
1158135446Strhodes
1159135446Strhodes/*
1160135446Strhodes * isc_mem_putanddetach() is the equivalent of:
1161135446Strhodes *
1162135446Strhodes * mctx = NULL;
1163135446Strhodes * isc_mem_attach(ptr->mctx, &mctx);
1164135446Strhodes * isc_mem_detach(&ptr->mctx);
1165135446Strhodes * isc_mem_put(mctx, ptr, sizeof(*ptr);
1166135446Strhodes * isc_mem_detach(&mctx);
1167135446Strhodes */
1168135446Strhodes
1169224092SdougbISC_MEMFUNC_SCOPE void
1170224092Sdougbisc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
1171224092Sdougb	isc__mem_t *ctx;
1172135446Strhodes	isc_boolean_t want_destroy = ISC_FALSE;
1173170222Sdougb	size_info *si;
1174170222Sdougb	size_t oldsize;
1175135446Strhodes
1176135446Strhodes	REQUIRE(ctxp != NULL);
1177224092Sdougb	ctx = (isc__mem_t *)*ctxp;
1178135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1179135446Strhodes	REQUIRE(ptr != NULL);
1180135446Strhodes
1181135446Strhodes	/*
1182135446Strhodes	 * Must be before mem_putunlocked() as ctxp is usually within
1183135446Strhodes	 * [ptr..ptr+size).
1184135446Strhodes	 */
1185135446Strhodes	*ctxp = NULL;
1186135446Strhodes
1187170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1188170222Sdougb		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1189170222Sdougb			si = &(((size_info *)ptr)[-1]);
1190170222Sdougb			oldsize = si->u.size - ALIGNMENT_SIZE;
1191170222Sdougb			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1192170222Sdougb				oldsize -= ALIGNMENT_SIZE;
1193170222Sdougb			INSIST(oldsize == size);
1194170222Sdougb		}
1195245163Serwin		isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS);
1196135446Strhodes
1197170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1198170222Sdougb		ctx->references--;
1199170222Sdougb		if (ctx->references == 0)
1200170222Sdougb			want_destroy = ISC_TRUE;
1201170222Sdougb		MCTXUNLOCK(ctx, &ctx->lock);
1202170222Sdougb		if (want_destroy)
1203170222Sdougb			destroy(ctx);
1204170222Sdougb
1205170222Sdougb		return;
1206170222Sdougb	}
1207170222Sdougb
1208170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1209170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1210170222Sdougb		mem_putunlocked(ctx, ptr, size);
1211170222Sdougb	} else {
1212170222Sdougb		mem_put(ctx, ptr, size);
1213170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1214170222Sdougb		mem_putstats(ctx, ptr, size);
1215170222Sdougb	}
1216170222Sdougb
1217135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1218135446Strhodes	INSIST(ctx->references > 0);
1219135446Strhodes	ctx->references--;
1220135446Strhodes	if (ctx->references == 0)
1221135446Strhodes		want_destroy = ISC_TRUE;
1222135446Strhodes
1223170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1224135446Strhodes
1225135446Strhodes	if (want_destroy)
1226135446Strhodes		destroy(ctx);
1227135446Strhodes}
1228135446Strhodes
1229224092SdougbISC_MEMFUNC_SCOPE void
1230224092Sdougbisc__mem_destroy(isc_mem_t **ctxp) {
1231224092Sdougb	isc__mem_t *ctx;
1232135446Strhodes
1233135446Strhodes	/*
1234135446Strhodes	 * This routine provides legacy support for callers who use mctxs
1235135446Strhodes	 * without attaching/detaching.
1236135446Strhodes	 */
1237135446Strhodes
1238135446Strhodes	REQUIRE(ctxp != NULL);
1239224092Sdougb	ctx = (isc__mem_t *)*ctxp;
1240135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1241135446Strhodes
1242170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1243135446Strhodes#if ISC_MEM_TRACKLINES
1244135446Strhodes	if (ctx->references != 1)
1245135446Strhodes		print_active(ctx, stderr);
1246135446Strhodes#endif
1247135446Strhodes	REQUIRE(ctx->references == 1);
1248135446Strhodes	ctx->references--;
1249170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1250135446Strhodes
1251135446Strhodes	destroy(ctx);
1252135446Strhodes
1253135446Strhodes	*ctxp = NULL;
1254135446Strhodes}
1255135446Strhodes
1256224092SdougbISC_MEMFUNC_SCOPE isc_result_t
1257224092Sdougbisc__mem_ondestroy(isc_mem_t *ctx0, isc_task_t *task, isc_event_t **event) {
1258224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1259135446Strhodes	isc_result_t res;
1260135446Strhodes
1261170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1262135446Strhodes	res = isc_ondestroy_register(&ctx->ondestroy, task, event);
1263170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1264135446Strhodes
1265135446Strhodes	return (res);
1266135446Strhodes}
1267135446Strhodes
1268224092SdougbISC_MEMFUNC_SCOPE void *
1269224092Sdougbisc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
1270224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1271135446Strhodes	void *ptr;
1272135446Strhodes	isc_boolean_t call_water = ISC_FALSE;
1273135446Strhodes
1274135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1275135446Strhodes
1276170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
1277224092Sdougb		return (isc__mem_allocate(ctx0, size FLARG_PASS));
1278135446Strhodes
1279170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1280170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1281170222Sdougb		ptr = mem_getunlocked(ctx, size);
1282170222Sdougb	} else {
1283170222Sdougb		ptr = mem_get(ctx, size);
1284170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1285170222Sdougb		if (ptr != NULL)
1286170222Sdougb			mem_getstats(ctx, size);
1287170222Sdougb	}
1288170222Sdougb
1289135446Strhodes	ADD_TRACE(ctx, ptr, size, file, line);
1290214586Sdougb	if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1291214586Sdougb	    !ctx->is_overmem) {
1292214586Sdougb		ctx->is_overmem = ISC_TRUE;
1293214586Sdougb	}
1294135446Strhodes	if (ctx->hi_water != 0U && !ctx->hi_called &&
1295135446Strhodes	    ctx->inuse > ctx->hi_water) {
1296135446Strhodes		call_water = ISC_TRUE;
1297135446Strhodes	}
1298135446Strhodes	if (ctx->inuse > ctx->maxinuse) {
1299135446Strhodes		ctx->maxinuse = ctx->inuse;
1300135446Strhodes		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1301135446Strhodes		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1302135446Strhodes			fprintf(stderr, "maxinuse = %lu\n",
1303135446Strhodes				(unsigned long)ctx->inuse);
1304135446Strhodes	}
1305170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1306135446Strhodes
1307135446Strhodes	if (call_water)
1308135446Strhodes		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1309135446Strhodes
1310135446Strhodes	return (ptr);
1311135446Strhodes}
1312135446Strhodes
1313224092SdougbISC_MEMFUNC_SCOPE void
1314224092Sdougbisc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1315224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1316135446Strhodes	isc_boolean_t call_water = ISC_FALSE;
1317170222Sdougb	size_info *si;
1318170222Sdougb	size_t oldsize;
1319135446Strhodes
1320135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1321135446Strhodes	REQUIRE(ptr != NULL);
1322135446Strhodes
1323170222Sdougb	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1324170222Sdougb		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1325170222Sdougb			si = &(((size_info *)ptr)[-1]);
1326170222Sdougb			oldsize = si->u.size - ALIGNMENT_SIZE;
1327170222Sdougb			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1328170222Sdougb				oldsize -= ALIGNMENT_SIZE;
1329170222Sdougb			INSIST(oldsize == size);
1330170222Sdougb		}
1331245163Serwin		isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS);
1332170222Sdougb		return;
1333170222Sdougb	}
1334135446Strhodes
1335170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1336170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1337170222Sdougb		mem_putunlocked(ctx, ptr, size);
1338170222Sdougb	} else {
1339170222Sdougb		mem_put(ctx, ptr, size);
1340170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1341170222Sdougb		mem_putstats(ctx, ptr, size);
1342170222Sdougb	}
1343170222Sdougb
1344135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1345135446Strhodes
1346135446Strhodes	/*
1347135446Strhodes	 * The check against ctx->lo_water == 0 is for the condition
1348135446Strhodes	 * when the context was pushed over hi_water but then had
1349135446Strhodes	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1350135446Strhodes	 */
1351214586Sdougb	if (ctx->is_overmem &&
1352214586Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1353214586Sdougb		ctx->is_overmem = ISC_FALSE;
1354214586Sdougb	}
1355186462Sdougb	if (ctx->hi_called &&
1356135446Strhodes	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1357135446Strhodes		if (ctx->water != NULL)
1358135446Strhodes			call_water = ISC_TRUE;
1359135446Strhodes	}
1360170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1361135446Strhodes
1362135446Strhodes	if (call_water)
1363135446Strhodes		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1364135446Strhodes}
1365135446Strhodes
1366224092SdougbISC_MEMFUNC_SCOPE void
1367224092Sdougbisc__mem_waterack(isc_mem_t *ctx0, int flag) {
1368224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1369224092Sdougb
1370186462Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1371186462Sdougb
1372186462Sdougb	MCTXLOCK(ctx, &ctx->lock);
1373186462Sdougb	if (flag == ISC_MEM_LOWATER)
1374186462Sdougb		ctx->hi_called = ISC_FALSE;
1375186462Sdougb	else if (flag == ISC_MEM_HIWATER)
1376186462Sdougb		ctx->hi_called = ISC_TRUE;
1377186462Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1378186462Sdougb}
1379186462Sdougb
1380135446Strhodes#if ISC_MEM_TRACKLINES
1381135446Strhodesstatic void
1382224092Sdougbprint_active(isc__mem_t *mctx, FILE *out) {
1383135446Strhodes	if (mctx->debuglist != NULL) {
1384135446Strhodes		debuglink_t *dl;
1385135446Strhodes		unsigned int i, j;
1386135446Strhodes		const char *format;
1387135446Strhodes		isc_boolean_t found;
1388135446Strhodes
1389193149Sdougb		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1390135446Strhodes					    ISC_MSG_DUMPALLOC,
1391135446Strhodes					    "Dump of all outstanding "
1392135446Strhodes					    "memory allocations:\n"));
1393135446Strhodes		found = ISC_FALSE;
1394135446Strhodes		format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1395186462Sdougb					ISC_MSG_PTRFILELINE,
1396135446Strhodes					"\tptr %p size %u file %s line %u\n");
1397135446Strhodes		for (i = 0; i <= mctx->max_size; i++) {
1398135446Strhodes			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
1399186462Sdougb
1400135446Strhodes			if (dl != NULL)
1401135446Strhodes				found = ISC_TRUE;
1402135446Strhodes
1403135446Strhodes			while (dl != NULL) {
1404135446Strhodes				for (j = 0; j < DEBUGLIST_COUNT; j++)
1405135446Strhodes					if (dl->ptr[j] != NULL)
1406135446Strhodes						fprintf(out, format,
1407135446Strhodes							dl->ptr[j],
1408135446Strhodes							dl->size[j],
1409135446Strhodes							dl->file[j],
1410135446Strhodes							dl->line[j]);
1411135446Strhodes				dl = ISC_LIST_NEXT(dl, link);
1412135446Strhodes			}
1413135446Strhodes		}
1414135446Strhodes		if (!found)
1415193149Sdougb			fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1416135446Strhodes						    ISC_MSG_NONE, "\tNone.\n"));
1417135446Strhodes	}
1418135446Strhodes}
1419135446Strhodes#endif
1420135446Strhodes
1421135446Strhodes/*
1422135446Strhodes * Print the stats[] on the stream "out" with suitable formatting.
1423135446Strhodes */
1424224092SdougbISC_MEMFUNC_SCOPE void
1425224092Sdougbisc__mem_stats(isc_mem_t *ctx0, FILE *out) {
1426224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1427135446Strhodes	size_t i;
1428135446Strhodes	const struct stats *s;
1429224092Sdougb	const isc__mempool_t *pool;
1430135446Strhodes
1431135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1432170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1433135446Strhodes
1434135446Strhodes	for (i = 0; i <= ctx->max_size; i++) {
1435135446Strhodes		s = &ctx->stats[i];
1436135446Strhodes
1437135446Strhodes		if (s->totalgets == 0U && s->gets == 0U)
1438135446Strhodes			continue;
1439135446Strhodes		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
1440135446Strhodes			(i == ctx->max_size) ? ">=" : "  ",
1441135446Strhodes			(unsigned long) i, s->totalgets, s->gets);
1442170222Sdougb		if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
1443170222Sdougb		    (s->blocks != 0U || s->freefrags != 0U))
1444135446Strhodes			fprintf(out, " (%lu bl, %lu ff)",
1445135446Strhodes				s->blocks, s->freefrags);
1446135446Strhodes		fputc('\n', out);
1447135446Strhodes	}
1448135446Strhodes
1449135446Strhodes	/*
1450135446Strhodes	 * Note that since a pool can be locked now, these stats might be
1451135446Strhodes	 * somewhat off if the pool is in active use at the time the stats
1452135446Strhodes	 * are dumped.  The link fields are protected by the isc_mem_t's
1453135446Strhodes	 * lock, however, so walking this list and extracting integers from
1454135446Strhodes	 * stats fields is always safe.
1455135446Strhodes	 */
1456135446Strhodes	pool = ISC_LIST_HEAD(ctx->pools);
1457135446Strhodes	if (pool != NULL) {
1458193149Sdougb		fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1459135446Strhodes					    ISC_MSG_POOLSTATS,
1460135446Strhodes					    "[Pool statistics]\n"));
1461135446Strhodes		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
1462135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1463135446Strhodes				       ISC_MSG_POOLNAME, "name"),
1464135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1465135446Strhodes				       ISC_MSG_POOLSIZE, "size"),
1466135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1467135446Strhodes				       ISC_MSG_POOLMAXALLOC, "maxalloc"),
1468135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1469135446Strhodes				       ISC_MSG_POOLALLOCATED, "allocated"),
1470135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1471135446Strhodes				       ISC_MSG_POOLFREECOUNT, "freecount"),
1472135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1473135446Strhodes				       ISC_MSG_POOLFREEMAX, "freemax"),
1474135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1475135446Strhodes				       ISC_MSG_POOLFILLCOUNT, "fillcount"),
1476135446Strhodes			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1477135446Strhodes				       ISC_MSG_POOLGETS, "gets"),
1478135446Strhodes			"L");
1479135446Strhodes	}
1480135446Strhodes	while (pool != NULL) {
1481135446Strhodes		fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
1482254402Serwin#if ISC_MEMPOOL_NAMES
1483254402Serwin			pool->name,
1484254402Serwin#else
1485254402Serwin			"(not tracked)",
1486254402Serwin#endif
1487254402Serwin			(unsigned long) pool->size, pool->maxalloc,
1488135446Strhodes			pool->allocated, pool->freecount, pool->freemax,
1489135446Strhodes			pool->fillcount, pool->gets,
1490135446Strhodes			(pool->lock == NULL ? "N" : "Y"));
1491135446Strhodes		pool = ISC_LIST_NEXT(pool, link);
1492135446Strhodes	}
1493135446Strhodes
1494135446Strhodes#if ISC_MEM_TRACKLINES
1495135446Strhodes	print_active(ctx, out);
1496135446Strhodes#endif
1497135446Strhodes
1498170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1499135446Strhodes}
1500135446Strhodes
1501135446Strhodes/*
1502135446Strhodes * Replacements for malloc() and free() -- they implicitly remember the
1503135446Strhodes * size of the object allocated (with some additional overhead).
1504135446Strhodes */
1505135446Strhodes
1506135446Strhodesstatic void *
1507224092Sdougbisc__mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
1508224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1509135446Strhodes	size_info *si;
1510135446Strhodes
1511135446Strhodes	size += ALIGNMENT_SIZE;
1512170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1513170222Sdougb		size += ALIGNMENT_SIZE;
1514170222Sdougb
1515170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
1516170222Sdougb		si = mem_getunlocked(ctx, size);
1517170222Sdougb	else
1518170222Sdougb		si = mem_get(ctx, size);
1519170222Sdougb
1520135446Strhodes	if (si == NULL)
1521135446Strhodes		return (NULL);
1522170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1523170222Sdougb		si->u.ctx = ctx;
1524170222Sdougb		si++;
1525170222Sdougb	}
1526135446Strhodes	si->u.size = size;
1527135446Strhodes	return (&si[1]);
1528135446Strhodes}
1529135446Strhodes
1530224092SdougbISC_MEMFUNC_SCOPE void *
1531224092Sdougbisc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) {
1532224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1533135446Strhodes	size_info *si;
1534170222Sdougb	isc_boolean_t call_water = ISC_FALSE;
1535135446Strhodes
1536135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1537135446Strhodes
1538170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1539170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1540224092Sdougb		si = isc__mem_allocateunlocked((isc_mem_t *)ctx, size);
1541170222Sdougb	} else {
1542224092Sdougb		si = isc__mem_allocateunlocked((isc_mem_t *)ctx, size);
1543170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1544170222Sdougb		if (si != NULL)
1545170222Sdougb			mem_getstats(ctx, si[-1].u.size);
1546170222Sdougb	}
1547135446Strhodes
1548135446Strhodes#if ISC_MEM_TRACKLINES
1549135446Strhodes	ADD_TRACE(ctx, si, si[-1].u.size, file, line);
1550135446Strhodes#endif
1551214586Sdougb	if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1552214586Sdougb	    !ctx->is_overmem) {
1553214586Sdougb		ctx->is_overmem = ISC_TRUE;
1554214586Sdougb	}
1555214586Sdougb
1556170222Sdougb	if (ctx->hi_water != 0U && !ctx->hi_called &&
1557170222Sdougb	    ctx->inuse > ctx->hi_water) {
1558170222Sdougb		ctx->hi_called = ISC_TRUE;
1559170222Sdougb		call_water = ISC_TRUE;
1560170222Sdougb	}
1561170222Sdougb	if (ctx->inuse > ctx->maxinuse) {
1562170222Sdougb		ctx->maxinuse = ctx->inuse;
1563170222Sdougb		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1564170222Sdougb		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1565170222Sdougb			fprintf(stderr, "maxinuse = %lu\n",
1566170222Sdougb				(unsigned long)ctx->inuse);
1567170222Sdougb	}
1568170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1569135446Strhodes
1570170222Sdougb	if (call_water)
1571170222Sdougb		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1572135446Strhodes
1573135446Strhodes	return (si);
1574135446Strhodes}
1575135446Strhodes
1576224092SdougbISC_MEMFUNC_SCOPE void *
1577224092Sdougbisc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1578224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1579193149Sdougb	void *new_ptr = NULL;
1580193149Sdougb	size_t oldsize, copysize;
1581193149Sdougb
1582193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1583193149Sdougb
1584193149Sdougb	/*
1585193149Sdougb	 * This function emulates the realloc(3) standard library function:
1586193149Sdougb	 * - if size > 0, allocate new memory; and if ptr is non NULL, copy
1587193149Sdougb	 *   as much of the old contents to the new buffer and free the old one.
1588193149Sdougb	 *   Note that when allocation fails the original pointer is intact;
1589193149Sdougb	 *   the caller must free it.
1590193149Sdougb	 * - if size is 0 and ptr is non NULL, simply free the given ptr.
1591193149Sdougb	 * - this function returns:
1592193149Sdougb	 *     pointer to the newly allocated memory, or
1593193149Sdougb	 *     NULL if allocation fails or doesn't happen.
1594193149Sdougb	 */
1595193149Sdougb	if (size > 0U) {
1596224092Sdougb		new_ptr = isc__mem_allocate(ctx0, size FLARG_PASS);
1597193149Sdougb		if (new_ptr != NULL && ptr != NULL) {
1598193149Sdougb			oldsize = (((size_info *)ptr)[-1]).u.size;
1599193149Sdougb			INSIST(oldsize >= ALIGNMENT_SIZE);
1600193149Sdougb			oldsize -= ALIGNMENT_SIZE;
1601245163Serwin			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1602245163Serwin				INSIST(oldsize >= ALIGNMENT_SIZE);
1603245163Serwin				oldsize -= ALIGNMENT_SIZE;
1604245163Serwin			}
1605245163Serwin			copysize = (oldsize > size) ? size : oldsize;
1606262706Serwin			memmove(new_ptr, ptr, copysize);
1607224092Sdougb			isc__mem_free(ctx0, ptr FLARG_PASS);
1608193149Sdougb		}
1609193149Sdougb	} else if (ptr != NULL)
1610224092Sdougb		isc__mem_free(ctx0, ptr FLARG_PASS);
1611193149Sdougb
1612193149Sdougb	return (new_ptr);
1613193149Sdougb}
1614193149Sdougb
1615224092SdougbISC_MEMFUNC_SCOPE void
1616224092Sdougbisc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) {
1617224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1618135446Strhodes	size_info *si;
1619135446Strhodes	size_t size;
1620170222Sdougb	isc_boolean_t call_water= ISC_FALSE;
1621135446Strhodes
1622135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1623135446Strhodes	REQUIRE(ptr != NULL);
1624135446Strhodes
1625170222Sdougb	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1626170222Sdougb		si = &(((size_info *)ptr)[-2]);
1627170222Sdougb		REQUIRE(si->u.ctx == ctx);
1628170222Sdougb		size = si[1].u.size;
1629170222Sdougb	} else {
1630170222Sdougb		si = &(((size_info *)ptr)[-1]);
1631170222Sdougb		size = si->u.size;
1632170222Sdougb	}
1633135446Strhodes
1634170222Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1635170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1636170222Sdougb		mem_putunlocked(ctx, si, size);
1637170222Sdougb	} else {
1638170222Sdougb		mem_put(ctx, si, size);
1639170222Sdougb		MCTXLOCK(ctx, &ctx->lock);
1640170222Sdougb		mem_putstats(ctx, si, size);
1641170222Sdougb	}
1642135446Strhodes
1643135446Strhodes	DELETE_TRACE(ctx, ptr, size, file, line);
1644135446Strhodes
1645170222Sdougb	/*
1646170222Sdougb	 * The check against ctx->lo_water == 0 is for the condition
1647170222Sdougb	 * when the context was pushed over hi_water but then had
1648170222Sdougb	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1649170222Sdougb	 */
1650214586Sdougb	if (ctx->is_overmem &&
1651214586Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1652214586Sdougb		ctx->is_overmem = ISC_FALSE;
1653214586Sdougb	}
1654214586Sdougb
1655186462Sdougb	if (ctx->hi_called &&
1656170222Sdougb	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1657170222Sdougb		ctx->hi_called = ISC_FALSE;
1658170222Sdougb
1659170222Sdougb		if (ctx->water != NULL)
1660170222Sdougb			call_water = ISC_TRUE;
1661170222Sdougb	}
1662170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1663170222Sdougb
1664170222Sdougb	if (call_water)
1665170222Sdougb		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1666135446Strhodes}
1667135446Strhodes
1668135446Strhodes
1669135446Strhodes/*
1670135446Strhodes * Other useful things.
1671135446Strhodes */
1672135446Strhodes
1673224092SdougbISC_MEMFUNC_SCOPE char *
1674224092Sdougbisc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) {
1675224092Sdougb	isc__mem_t *mctx = (isc__mem_t *)mctx0;
1676135446Strhodes	size_t len;
1677135446Strhodes	char *ns;
1678135446Strhodes
1679135446Strhodes	REQUIRE(VALID_CONTEXT(mctx));
1680135446Strhodes	REQUIRE(s != NULL);
1681135446Strhodes
1682135446Strhodes	len = strlen(s);
1683135446Strhodes
1684224092Sdougb	ns = isc___mem_allocate((isc_mem_t *)mctx, len + 1 FLARG_PASS);
1685135446Strhodes
1686135446Strhodes	if (ns != NULL)
1687135446Strhodes		strncpy(ns, s, len + 1);
1688135446Strhodes
1689135446Strhodes	return (ns);
1690135446Strhodes}
1691135446Strhodes
1692224092SdougbISC_MEMFUNC_SCOPE void
1693224092Sdougbisc__mem_setdestroycheck(isc_mem_t *ctx0, isc_boolean_t flag) {
1694224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1695224092Sdougb
1696135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1697170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1698135446Strhodes
1699135446Strhodes	ctx->checkfree = flag;
1700135446Strhodes
1701170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1702135446Strhodes}
1703135446Strhodes
1704135446Strhodes/*
1705135446Strhodes * Quotas
1706135446Strhodes */
1707135446Strhodes
1708224092SdougbISC_MEMFUNC_SCOPE void
1709224092Sdougbisc__mem_setquota(isc_mem_t *ctx0, size_t quota) {
1710224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1711224092Sdougb
1712135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1713170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1714135446Strhodes
1715135446Strhodes	ctx->quota = quota;
1716135446Strhodes
1717170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1718135446Strhodes}
1719135446Strhodes
1720224092SdougbISC_MEMFUNC_SCOPE size_t
1721224092Sdougbisc__mem_getquota(isc_mem_t *ctx0) {
1722224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1723135446Strhodes	size_t quota;
1724135446Strhodes
1725135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1726170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1727135446Strhodes
1728135446Strhodes	quota = ctx->quota;
1729135446Strhodes
1730170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1731135446Strhodes
1732135446Strhodes	return (quota);
1733135446Strhodes}
1734135446Strhodes
1735224092SdougbISC_MEMFUNC_SCOPE size_t
1736224092Sdougbisc__mem_inuse(isc_mem_t *ctx0) {
1737224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1738135446Strhodes	size_t inuse;
1739135446Strhodes
1740135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1741170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1742135446Strhodes
1743135446Strhodes	inuse = ctx->inuse;
1744135446Strhodes
1745170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1746135446Strhodes
1747135446Strhodes	return (inuse);
1748135446Strhodes}
1749135446Strhodes
1750224092SdougbISC_MEMFUNC_SCOPE void
1751224092Sdougbisc__mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg,
1752186462Sdougb		 size_t hiwater, size_t lowater)
1753135446Strhodes{
1754224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1755170222Sdougb	isc_boolean_t callwater = ISC_FALSE;
1756170222Sdougb	isc_mem_water_t oldwater;
1757170222Sdougb	void *oldwater_arg;
1758170222Sdougb
1759135446Strhodes	REQUIRE(VALID_CONTEXT(ctx));
1760135446Strhodes	REQUIRE(hiwater >= lowater);
1761135446Strhodes
1762170222Sdougb	MCTXLOCK(ctx, &ctx->lock);
1763170222Sdougb	oldwater = ctx->water;
1764170222Sdougb	oldwater_arg = ctx->water_arg;
1765135446Strhodes	if (water == NULL) {
1766170222Sdougb		callwater = ctx->hi_called;
1767135446Strhodes		ctx->water = NULL;
1768135446Strhodes		ctx->water_arg = NULL;
1769135446Strhodes		ctx->hi_water = 0;
1770135446Strhodes		ctx->lo_water = 0;
1771135446Strhodes		ctx->hi_called = ISC_FALSE;
1772135446Strhodes	} else {
1773170222Sdougb		if (ctx->hi_called &&
1774170222Sdougb		    (ctx->water != water || ctx->water_arg != water_arg ||
1775170222Sdougb		     ctx->inuse < lowater || lowater == 0U))
1776170222Sdougb			callwater = ISC_TRUE;
1777135446Strhodes		ctx->water = water;
1778135446Strhodes		ctx->water_arg = water_arg;
1779135446Strhodes		ctx->hi_water = hiwater;
1780135446Strhodes		ctx->lo_water = lowater;
1781135446Strhodes		ctx->hi_called = ISC_FALSE;
1782135446Strhodes	}
1783170222Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
1784186462Sdougb
1785170222Sdougb	if (callwater && oldwater != NULL)
1786170222Sdougb		(oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1787135446Strhodes}
1788135446Strhodes
1789224092SdougbISC_MEMFUNC_SCOPE isc_boolean_t
1790224092Sdougbisc__mem_isovermem(isc_mem_t *ctx0) {
1791224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1792224092Sdougb
1793214586Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1794214586Sdougb
1795214586Sdougb	/*
1796214586Sdougb	 * We don't bother to lock the context because 100% accuracy isn't
1797214586Sdougb	 * necessary (and even if we locked the context the returned value
1798214586Sdougb	 * could be different from the actual state when it's used anyway)
1799214586Sdougb	 */
1800214586Sdougb	return (ctx->is_overmem);
1801214586Sdougb}
1802214586Sdougb
1803224092SdougbISC_MEMFUNC_SCOPE void
1804224092Sdougbisc__mem_setname(isc_mem_t *ctx0, const char *name, void *tag) {
1805224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1806224092Sdougb
1807193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1808193149Sdougb
1809193149Sdougb	LOCK(&ctx->lock);
1810193149Sdougb	memset(ctx->name, 0, sizeof(ctx->name));
1811193149Sdougb	strncpy(ctx->name, name, sizeof(ctx->name) - 1);
1812193149Sdougb	ctx->tag = tag;
1813193149Sdougb	UNLOCK(&ctx->lock);
1814193149Sdougb}
1815193149Sdougb
1816224092SdougbISC_MEMFUNC_SCOPE const char *
1817224092Sdougbisc__mem_getname(isc_mem_t *ctx0) {
1818224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1819224092Sdougb
1820193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1821193149Sdougb
1822193149Sdougb	return (ctx->name);
1823193149Sdougb}
1824193149Sdougb
1825224092SdougbISC_MEMFUNC_SCOPE void *
1826224092Sdougbisc__mem_gettag(isc_mem_t *ctx0) {
1827224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
1828224092Sdougb
1829193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
1830193149Sdougb
1831193149Sdougb	return (ctx->tag);
1832193149Sdougb}
1833193149Sdougb
1834135446Strhodes/*
1835135446Strhodes * Memory pool stuff
1836135446Strhodes */
1837135446Strhodes
1838224092SdougbISC_MEMFUNC_SCOPE isc_result_t
1839224092Sdougbisc__mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) {
1840224092Sdougb	isc__mem_t *mctx = (isc__mem_t *)mctx0;
1841224092Sdougb	isc__mempool_t *mpctx;
1842135446Strhodes
1843135446Strhodes	REQUIRE(VALID_CONTEXT(mctx));
1844135446Strhodes	REQUIRE(size > 0U);
1845135446Strhodes	REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1846135446Strhodes
1847135446Strhodes	/*
1848135446Strhodes	 * Allocate space for this pool, initialize values, and if all works
1849135446Strhodes	 * well, attach to the memory context.
1850135446Strhodes	 */
1851224092Sdougb	mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t));
1852135446Strhodes	if (mpctx == NULL)
1853135446Strhodes		return (ISC_R_NOMEMORY);
1854135446Strhodes
1855224092Sdougb	mpctx->common.methods = (isc_mempoolmethods_t *)&mempoolmethods;
1856224092Sdougb	mpctx->common.impmagic = MEMPOOL_MAGIC;
1857224092Sdougb	mpctx->common.magic = ISCAPI_MPOOL_MAGIC;
1858135446Strhodes	mpctx->lock = NULL;
1859135446Strhodes	mpctx->mctx = mctx;
1860135446Strhodes	mpctx->size = size;
1861135446Strhodes	mpctx->maxalloc = UINT_MAX;
1862135446Strhodes	mpctx->allocated = 0;
1863135446Strhodes	mpctx->freecount = 0;
1864135446Strhodes	mpctx->freemax = 1;
1865135446Strhodes	mpctx->fillcount = 1;
1866135446Strhodes	mpctx->gets = 0;
1867135446Strhodes#if ISC_MEMPOOL_NAMES
1868135446Strhodes	mpctx->name[0] = 0;
1869135446Strhodes#endif
1870135446Strhodes	mpctx->items = NULL;
1871135446Strhodes
1872224092Sdougb	*mpctxp = (isc_mempool_t *)mpctx;
1873135446Strhodes
1874170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1875135446Strhodes	ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1876193149Sdougb	mctx->poolcnt++;
1877170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1878135446Strhodes
1879135446Strhodes	return (ISC_R_SUCCESS);
1880135446Strhodes}
1881135446Strhodes
1882224092SdougbISC_MEMFUNC_SCOPE void
1883224092Sdougbisc__mempool_setname(isc_mempool_t *mpctx0, const char *name) {
1884224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1885224092Sdougb
1886135446Strhodes	REQUIRE(name != NULL);
1887224092Sdougb	REQUIRE(VALID_MEMPOOL(mpctx));
1888135446Strhodes
1889135446Strhodes#if ISC_MEMPOOL_NAMES
1890135446Strhodes	if (mpctx->lock != NULL)
1891135446Strhodes		LOCK(mpctx->lock);
1892135446Strhodes
1893135446Strhodes	strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
1894135446Strhodes	mpctx->name[sizeof(mpctx->name) - 1] = '\0';
1895135446Strhodes
1896135446Strhodes	if (mpctx->lock != NULL)
1897135446Strhodes		UNLOCK(mpctx->lock);
1898135446Strhodes#else
1899135446Strhodes	UNUSED(mpctx);
1900135446Strhodes	UNUSED(name);
1901135446Strhodes#endif
1902135446Strhodes}
1903135446Strhodes
1904224092SdougbISC_MEMFUNC_SCOPE void
1905224092Sdougbisc__mempool_destroy(isc_mempool_t **mpctxp) {
1906224092Sdougb	isc__mempool_t *mpctx;
1907224092Sdougb	isc__mem_t *mctx;
1908135446Strhodes	isc_mutex_t *lock;
1909135446Strhodes	element *item;
1910135446Strhodes
1911135446Strhodes	REQUIRE(mpctxp != NULL);
1912224092Sdougb	mpctx = (isc__mempool_t *)*mpctxp;
1913135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1914135446Strhodes#if ISC_MEMPOOL_NAMES
1915135446Strhodes	if (mpctx->allocated > 0)
1916135446Strhodes		UNEXPECTED_ERROR(__FILE__, __LINE__,
1917224092Sdougb				 "isc__mempool_destroy(): mempool %s "
1918135446Strhodes				 "leaked memory",
1919135446Strhodes				 mpctx->name);
1920135446Strhodes#endif
1921135446Strhodes	REQUIRE(mpctx->allocated == 0);
1922135446Strhodes
1923135446Strhodes	mctx = mpctx->mctx;
1924135446Strhodes
1925135446Strhodes	lock = mpctx->lock;
1926135446Strhodes
1927135446Strhodes	if (lock != NULL)
1928135446Strhodes		LOCK(lock);
1929135446Strhodes
1930135446Strhodes	/*
1931135446Strhodes	 * Return any items on the free list
1932135446Strhodes	 */
1933170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1934135446Strhodes	while (mpctx->items != NULL) {
1935135446Strhodes		INSIST(mpctx->freecount > 0);
1936135446Strhodes		mpctx->freecount--;
1937135446Strhodes		item = mpctx->items;
1938135446Strhodes		mpctx->items = item->next;
1939135446Strhodes
1940170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1941170222Sdougb			mem_putunlocked(mctx, item, mpctx->size);
1942170222Sdougb		} else {
1943170222Sdougb			mem_put(mctx, item, mpctx->size);
1944170222Sdougb			mem_putstats(mctx, item, mpctx->size);
1945170222Sdougb		}
1946135446Strhodes	}
1947170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1948135446Strhodes
1949135446Strhodes	/*
1950135446Strhodes	 * Remove our linked list entry from the memory context.
1951135446Strhodes	 */
1952170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
1953135446Strhodes	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1954193149Sdougb	mctx->poolcnt--;
1955170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
1956135446Strhodes
1957224092Sdougb	mpctx->common.impmagic = 0;
1958224092Sdougb	mpctx->common.magic = 0;
1959135446Strhodes
1960224092Sdougb	isc_mem_put((isc_mem_t *)mpctx->mctx, mpctx, sizeof(isc__mempool_t));
1961135446Strhodes
1962135446Strhodes	if (lock != NULL)
1963135446Strhodes		UNLOCK(lock);
1964135446Strhodes
1965135446Strhodes	*mpctxp = NULL;
1966135446Strhodes}
1967135446Strhodes
1968224092SdougbISC_MEMFUNC_SCOPE void
1969224092Sdougbisc__mempool_associatelock(isc_mempool_t *mpctx0, isc_mutex_t *lock) {
1970224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1971224092Sdougb
1972135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1973135446Strhodes	REQUIRE(mpctx->lock == NULL);
1974135446Strhodes	REQUIRE(lock != NULL);
1975135446Strhodes
1976135446Strhodes	mpctx->lock = lock;
1977135446Strhodes}
1978135446Strhodes
1979224092SdougbISC_MEMFUNC_SCOPE void *
1980224092Sdougbisc___mempool_get(isc_mempool_t *mpctx0 FLARG) {
1981224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1982135446Strhodes	element *item;
1983224092Sdougb	isc__mem_t *mctx;
1984135446Strhodes	unsigned int i;
1985135446Strhodes
1986135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
1987135446Strhodes
1988135446Strhodes	mctx = mpctx->mctx;
1989135446Strhodes
1990135446Strhodes	if (mpctx->lock != NULL)
1991135446Strhodes		LOCK(mpctx->lock);
1992135446Strhodes
1993135446Strhodes	/*
1994135446Strhodes	 * Don't let the caller go over quota
1995135446Strhodes	 */
1996135446Strhodes	if (mpctx->allocated >= mpctx->maxalloc) {
1997135446Strhodes		item = NULL;
1998135446Strhodes		goto out;
1999135446Strhodes	}
2000135446Strhodes
2001135446Strhodes	/*
2002135446Strhodes	 * if we have a free list item, return the first here
2003135446Strhodes	 */
2004135446Strhodes	item = mpctx->items;
2005135446Strhodes	if (item != NULL) {
2006135446Strhodes		mpctx->items = item->next;
2007135446Strhodes		INSIST(mpctx->freecount > 0);
2008135446Strhodes		mpctx->freecount--;
2009135446Strhodes		mpctx->gets++;
2010135446Strhodes		mpctx->allocated++;
2011135446Strhodes		goto out;
2012135446Strhodes	}
2013135446Strhodes
2014135446Strhodes	/*
2015135446Strhodes	 * We need to dip into the well.  Lock the memory context here and
2016135446Strhodes	 * fill up our free list.
2017135446Strhodes	 */
2018170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
2019135446Strhodes	for (i = 0; i < mpctx->fillcount; i++) {
2020170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2021170222Sdougb			item = mem_getunlocked(mctx, mpctx->size);
2022170222Sdougb		} else {
2023170222Sdougb			item = mem_get(mctx, mpctx->size);
2024170222Sdougb			if (item != NULL)
2025170222Sdougb				mem_getstats(mctx, mpctx->size);
2026170222Sdougb		}
2027135446Strhodes		if (item == NULL)
2028135446Strhodes			break;
2029135446Strhodes		item->next = mpctx->items;
2030135446Strhodes		mpctx->items = item;
2031135446Strhodes		mpctx->freecount++;
2032135446Strhodes	}
2033170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
2034135446Strhodes
2035135446Strhodes	/*
2036135446Strhodes	 * If we didn't get any items, return NULL.
2037135446Strhodes	 */
2038135446Strhodes	item = mpctx->items;
2039135446Strhodes	if (item == NULL)
2040135446Strhodes		goto out;
2041135446Strhodes
2042135446Strhodes	mpctx->items = item->next;
2043135446Strhodes	mpctx->freecount--;
2044135446Strhodes	mpctx->gets++;
2045135446Strhodes	mpctx->allocated++;
2046135446Strhodes
2047135446Strhodes out:
2048135446Strhodes	if (mpctx->lock != NULL)
2049135446Strhodes		UNLOCK(mpctx->lock);
2050135446Strhodes
2051135446Strhodes#if ISC_MEM_TRACKLINES
2052135446Strhodes	if (item != NULL) {
2053170222Sdougb		MCTXLOCK(mctx, &mctx->lock);
2054135446Strhodes		ADD_TRACE(mctx, item, mpctx->size, file, line);
2055170222Sdougb		MCTXUNLOCK(mctx, &mctx->lock);
2056135446Strhodes	}
2057135446Strhodes#endif /* ISC_MEM_TRACKLINES */
2058135446Strhodes
2059135446Strhodes	return (item);
2060135446Strhodes}
2061135446Strhodes
2062224092SdougbISC_MEMFUNC_SCOPE void
2063224092Sdougbisc___mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
2064224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2065224092Sdougb	isc__mem_t *mctx;
2066135446Strhodes	element *item;
2067135446Strhodes
2068135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2069135446Strhodes	REQUIRE(mem != NULL);
2070135446Strhodes
2071135446Strhodes	mctx = mpctx->mctx;
2072135446Strhodes
2073135446Strhodes	if (mpctx->lock != NULL)
2074135446Strhodes		LOCK(mpctx->lock);
2075135446Strhodes
2076135446Strhodes	INSIST(mpctx->allocated > 0);
2077135446Strhodes	mpctx->allocated--;
2078135446Strhodes
2079135446Strhodes#if ISC_MEM_TRACKLINES
2080170222Sdougb	MCTXLOCK(mctx, &mctx->lock);
2081135446Strhodes	DELETE_TRACE(mctx, mem, mpctx->size, file, line);
2082170222Sdougb	MCTXUNLOCK(mctx, &mctx->lock);
2083135446Strhodes#endif /* ISC_MEM_TRACKLINES */
2084135446Strhodes
2085135446Strhodes	/*
2086135446Strhodes	 * If our free list is full, return this to the mctx directly.
2087135446Strhodes	 */
2088135446Strhodes	if (mpctx->freecount >= mpctx->freemax) {
2089170222Sdougb		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2090170222Sdougb			MCTXLOCK(mctx, &mctx->lock);
2091170222Sdougb			mem_putunlocked(mctx, mem, mpctx->size);
2092170222Sdougb			MCTXUNLOCK(mctx, &mctx->lock);
2093170222Sdougb		} else {
2094170222Sdougb			mem_put(mctx, mem, mpctx->size);
2095170222Sdougb			MCTXLOCK(mctx, &mctx->lock);
2096170222Sdougb			mem_putstats(mctx, mem, mpctx->size);
2097170222Sdougb			MCTXUNLOCK(mctx, &mctx->lock);
2098170222Sdougb		}
2099135446Strhodes		if (mpctx->lock != NULL)
2100135446Strhodes			UNLOCK(mpctx->lock);
2101135446Strhodes		return;
2102135446Strhodes	}
2103135446Strhodes
2104135446Strhodes	/*
2105135446Strhodes	 * Otherwise, attach it to our free list and bump the counter.
2106135446Strhodes	 */
2107135446Strhodes	mpctx->freecount++;
2108135446Strhodes	item = (element *)mem;
2109135446Strhodes	item->next = mpctx->items;
2110135446Strhodes	mpctx->items = item;
2111135446Strhodes
2112135446Strhodes	if (mpctx->lock != NULL)
2113135446Strhodes		UNLOCK(mpctx->lock);
2114135446Strhodes}
2115135446Strhodes
2116135446Strhodes/*
2117135446Strhodes * Quotas
2118135446Strhodes */
2119135446Strhodes
2120224092SdougbISC_MEMFUNC_SCOPE void
2121224092Sdougbisc__mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) {
2122224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2123224092Sdougb
2124135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2125135446Strhodes
2126135446Strhodes	if (mpctx->lock != NULL)
2127135446Strhodes		LOCK(mpctx->lock);
2128135446Strhodes
2129135446Strhodes	mpctx->freemax = limit;
2130135446Strhodes
2131135446Strhodes	if (mpctx->lock != NULL)
2132135446Strhodes		UNLOCK(mpctx->lock);
2133135446Strhodes}
2134135446Strhodes
2135224092SdougbISC_MEMFUNC_SCOPE unsigned int
2136224092Sdougbisc__mempool_getfreemax(isc_mempool_t *mpctx0) {
2137224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2138135446Strhodes	unsigned int freemax;
2139135446Strhodes
2140135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2141135446Strhodes
2142135446Strhodes	if (mpctx->lock != NULL)
2143135446Strhodes		LOCK(mpctx->lock);
2144135446Strhodes
2145135446Strhodes	freemax = mpctx->freemax;
2146135446Strhodes
2147135446Strhodes	if (mpctx->lock != NULL)
2148135446Strhodes		UNLOCK(mpctx->lock);
2149135446Strhodes
2150135446Strhodes	return (freemax);
2151135446Strhodes}
2152135446Strhodes
2153224092SdougbISC_MEMFUNC_SCOPE unsigned int
2154224092Sdougbisc__mempool_getfreecount(isc_mempool_t *mpctx0) {
2155224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2156135446Strhodes	unsigned int freecount;
2157135446Strhodes
2158135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2159135446Strhodes
2160135446Strhodes	if (mpctx->lock != NULL)
2161135446Strhodes		LOCK(mpctx->lock);
2162135446Strhodes
2163135446Strhodes	freecount = mpctx->freecount;
2164135446Strhodes
2165135446Strhodes	if (mpctx->lock != NULL)
2166135446Strhodes		UNLOCK(mpctx->lock);
2167135446Strhodes
2168135446Strhodes	return (freecount);
2169135446Strhodes}
2170135446Strhodes
2171224092SdougbISC_MEMFUNC_SCOPE void
2172224092Sdougbisc__mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) {
2173224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2174224092Sdougb
2175135446Strhodes	REQUIRE(limit > 0);
2176135446Strhodes
2177135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2178135446Strhodes
2179135446Strhodes	if (mpctx->lock != NULL)
2180135446Strhodes		LOCK(mpctx->lock);
2181135446Strhodes
2182135446Strhodes	mpctx->maxalloc = limit;
2183135446Strhodes
2184135446Strhodes	if (mpctx->lock != NULL)
2185135446Strhodes		UNLOCK(mpctx->lock);
2186135446Strhodes}
2187135446Strhodes
2188224092SdougbISC_MEMFUNC_SCOPE unsigned int
2189224092Sdougbisc__mempool_getmaxalloc(isc_mempool_t *mpctx0) {
2190224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2191135446Strhodes	unsigned int maxalloc;
2192135446Strhodes
2193135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2194135446Strhodes
2195135446Strhodes	if (mpctx->lock != NULL)
2196135446Strhodes		LOCK(mpctx->lock);
2197135446Strhodes
2198135446Strhodes	maxalloc = mpctx->maxalloc;
2199135446Strhodes
2200135446Strhodes	if (mpctx->lock != NULL)
2201135446Strhodes		UNLOCK(mpctx->lock);
2202135446Strhodes
2203135446Strhodes	return (maxalloc);
2204135446Strhodes}
2205135446Strhodes
2206224092SdougbISC_MEMFUNC_SCOPE unsigned int
2207224092Sdougbisc__mempool_getallocated(isc_mempool_t *mpctx0) {
2208224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2209135446Strhodes	unsigned int allocated;
2210135446Strhodes
2211135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2212135446Strhodes
2213135446Strhodes	if (mpctx->lock != NULL)
2214135446Strhodes		LOCK(mpctx->lock);
2215135446Strhodes
2216135446Strhodes	allocated = mpctx->allocated;
2217135446Strhodes
2218135446Strhodes	if (mpctx->lock != NULL)
2219135446Strhodes		UNLOCK(mpctx->lock);
2220135446Strhodes
2221135446Strhodes	return (allocated);
2222135446Strhodes}
2223135446Strhodes
2224224092SdougbISC_MEMFUNC_SCOPE void
2225224092Sdougbisc__mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) {
2226224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2227224092Sdougb
2228135446Strhodes	REQUIRE(limit > 0);
2229135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2230135446Strhodes
2231135446Strhodes	if (mpctx->lock != NULL)
2232135446Strhodes		LOCK(mpctx->lock);
2233135446Strhodes
2234135446Strhodes	mpctx->fillcount = limit;
2235135446Strhodes
2236135446Strhodes	if (mpctx->lock != NULL)
2237135446Strhodes		UNLOCK(mpctx->lock);
2238135446Strhodes}
2239135446Strhodes
2240224092SdougbISC_MEMFUNC_SCOPE unsigned int
2241224092Sdougbisc__mempool_getfillcount(isc_mempool_t *mpctx0) {
2242224092Sdougb	isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2243224092Sdougb
2244135446Strhodes	unsigned int fillcount;
2245135446Strhodes
2246135446Strhodes	REQUIRE(VALID_MEMPOOL(mpctx));
2247135446Strhodes
2248135446Strhodes	if (mpctx->lock != NULL)
2249135446Strhodes		LOCK(mpctx->lock);
2250135446Strhodes
2251135446Strhodes	fillcount = mpctx->fillcount;
2252135446Strhodes
2253135446Strhodes	if (mpctx->lock != NULL)
2254135446Strhodes		UNLOCK(mpctx->lock);
2255135446Strhodes
2256135446Strhodes	return (fillcount);
2257135446Strhodes}
2258170222Sdougb
2259224092Sdougb#ifdef USE_MEMIMPREGISTER
2260224092Sdougbisc_result_t
2261224092Sdougbisc__mem_register() {
2262224092Sdougb	return (isc_mem_register(isc__mem_create2));
2263224092Sdougb}
2264224092Sdougb#endif
2265170222Sdougb
2266224092Sdougb#ifdef BIND9
2267224092SdougbISC_MEMFUNC_SCOPE void
2268224092Sdougbisc__mem_printactive(isc_mem_t *ctx0, FILE *file) {
2269254402Serwin#if ISC_MEM_TRACKLINES
2270224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
2271224092Sdougb
2272170222Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2273170222Sdougb	REQUIRE(file != NULL);
2274170222Sdougb
2275254402Serwin	print_active(ctx, file);
2276254402Serwin#else
2277254402Serwin	UNUSED(ctx0);
2278170222Sdougb	UNUSED(file);
2279170222Sdougb#endif
2280170222Sdougb}
2281170222Sdougb
2282224092SdougbISC_MEMFUNC_SCOPE void
2283224092Sdougbisc__mem_printallactive(FILE *file) {
2284170222Sdougb#if !ISC_MEM_TRACKLINES
2285170222Sdougb	UNUSED(file);
2286170222Sdougb#else
2287224092Sdougb	isc__mem_t *ctx;
2288170222Sdougb
2289170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2290170222Sdougb
2291170222Sdougb	LOCK(&lock);
2292170222Sdougb	for (ctx = ISC_LIST_HEAD(contexts);
2293170222Sdougb	     ctx != NULL;
2294170222Sdougb	     ctx = ISC_LIST_NEXT(ctx, link)) {
2295170222Sdougb		fprintf(file, "context: %p\n", ctx);
2296170222Sdougb		print_active(ctx, file);
2297170222Sdougb	}
2298170222Sdougb	UNLOCK(&lock);
2299170222Sdougb#endif
2300170222Sdougb}
2301170222Sdougb
2302224092SdougbISC_MEMFUNC_SCOPE void
2303224092Sdougbisc__mem_checkdestroyed(FILE *file) {
2304254402Serwin#if !ISC_MEM_TRACKLINES
2305254402Serwin	UNUSED(file);
2306254402Serwin#endif
2307170222Sdougb
2308170222Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2309170222Sdougb
2310170222Sdougb	LOCK(&lock);
2311170222Sdougb	if (!ISC_LIST_EMPTY(contexts))  {
2312170222Sdougb#if ISC_MEM_TRACKLINES
2313224092Sdougb		isc__mem_t *ctx;
2314170222Sdougb
2315170222Sdougb		for (ctx = ISC_LIST_HEAD(contexts);
2316170222Sdougb		     ctx != NULL;
2317170222Sdougb		     ctx = ISC_LIST_NEXT(ctx, link)) {
2318170222Sdougb			fprintf(file, "context: %p\n", ctx);
2319170222Sdougb			print_active(ctx, file);
2320170222Sdougb		}
2321170222Sdougb		fflush(file);
2322170222Sdougb#endif
2323174187Sdougb		INSIST(0);
2324170222Sdougb	}
2325170222Sdougb	UNLOCK(&lock);
2326170222Sdougb}
2327193149Sdougb
2328224092SdougbISC_MEMFUNC_SCOPE unsigned int
2329224092Sdougbisc_mem_references(isc_mem_t *ctx0) {
2330224092Sdougb	isc__mem_t *ctx = (isc__mem_t *)ctx0;
2331193149Sdougb	unsigned int references;
2332224092Sdougb
2333193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2334193149Sdougb
2335193149Sdougb	MCTXLOCK(ctx, &ctx->lock);
2336193149Sdougb	references = ctx->references;
2337193149Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
2338193149Sdougb
2339193149Sdougb	return (references);
2340193149Sdougb}
2341193149Sdougb
2342193149Sdougb#ifdef HAVE_LIBXML2
2343193149Sdougb
2344193149Sdougbtypedef struct summarystat {
2345193149Sdougb	isc_uint64_t	total;
2346193149Sdougb	isc_uint64_t	inuse;
2347193149Sdougb	isc_uint64_t	blocksize;
2348193149Sdougb	isc_uint64_t	contextsize;
2349193149Sdougb} summarystat_t;
2350193149Sdougb
2351254402Serwin#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
2352254402Serwinstatic int
2353224092Sdougbrenderctx(isc__mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
2354254402Serwin	int xmlrc;
2355254402Serwin
2356193149Sdougb	REQUIRE(VALID_CONTEXT(ctx));
2357193149Sdougb
2358254402Serwin	MCTXLOCK(ctx, &ctx->lock);
2359193149Sdougb
2360254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context"));
2361193149Sdougb
2362254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
2363254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx));
2364254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* id */
2365254402Serwin
2366193149Sdougb	if (ctx->name[0] != 0) {
2367254402Serwin		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
2368254402Serwin		TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name));
2369254402Serwin		TRY0(xmlTextWriterEndElement(writer)); /* name */
2370193149Sdougb	}
2371193149Sdougb
2372193149Sdougb	summary->contextsize += sizeof(*ctx) +
2373193149Sdougb		(ctx->max_size + 1) * sizeof(struct stats) +
2374193149Sdougb		ctx->max_size * sizeof(element *) +
2375193149Sdougb		ctx->basic_table_count * sizeof(char *);
2376193149Sdougb#if ISC_MEM_TRACKLINES
2377193149Sdougb	if (ctx->debuglist != NULL) {
2378193149Sdougb		summary->contextsize +=
2379193149Sdougb			(ctx->max_size + 1) * sizeof(debuglist_t) +
2380193149Sdougb			ctx->debuglistcnt * sizeof(debuglink_t);
2381193149Sdougb	}
2382193149Sdougb#endif
2383254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"));
2384254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer, "%d", ctx->references));
2385254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* references */
2386193149Sdougb
2387193149Sdougb	summary->total += ctx->total;
2388254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total"));
2389254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2390254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2391254402Serwin					    (isc_uint64_t)ctx->total));
2392254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* total */
2393193149Sdougb
2394193149Sdougb	summary->inuse += ctx->inuse;
2395254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse"));
2396254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2397254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2398254402Serwin					    (isc_uint64_t)ctx->inuse));
2399254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* inuse */
2400193149Sdougb
2401254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse"));
2402254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2403254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2404254402Serwin					    (isc_uint64_t)ctx->maxinuse));
2405254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */
2406193149Sdougb
2407254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize"));
2408193149Sdougb	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2409193149Sdougb		summary->blocksize += ctx->basic_table_count *
2410193149Sdougb			NUM_BASIC_BLOCKS * ctx->mem_target;
2411254402Serwin		TRY0(xmlTextWriterWriteFormatString(writer,
2412193149Sdougb					       "%" ISC_PRINT_QUADFORMAT "u",
2413193149Sdougb					       (isc_uint64_t)
2414193149Sdougb					       ctx->basic_table_count *
2415193149Sdougb					       NUM_BASIC_BLOCKS *
2416254402Serwin					       ctx->mem_target));
2417193149Sdougb	} else
2418254402Serwin		TRY0(xmlTextWriterWriteFormatString(writer, "%s", "-"));
2419254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* blocksize */
2420193149Sdougb
2421254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools"));
2422254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt));
2423254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* pools */
2424193149Sdougb	summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2425193149Sdougb
2426254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater"));
2427254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2428254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2429254402Serwin					    (isc_uint64_t)ctx->hi_water));
2430254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* hiwater */
2431193149Sdougb
2432254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater"));
2433254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2434254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2435254402Serwin					    (isc_uint64_t)ctx->lo_water));
2436254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* lowater */
2437193149Sdougb
2438254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* context */
2439254402Serwin
2440254402Serwin error:
2441193149Sdougb	MCTXUNLOCK(ctx, &ctx->lock);
2442193149Sdougb
2443254402Serwin	return (xmlrc);
2444193149Sdougb}
2445193149Sdougb
2446254402Serwinint
2447193149Sdougbisc_mem_renderxml(xmlTextWriterPtr writer) {
2448224092Sdougb	isc__mem_t *ctx;
2449193149Sdougb	summarystat_t summary;
2450193149Sdougb	isc_uint64_t lost;
2451254402Serwin	int xmlrc;
2452193149Sdougb
2453193149Sdougb	memset(&summary, 0, sizeof(summary));
2454193149Sdougb
2455254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts"));
2456193149Sdougb
2457193149Sdougb	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2458193149Sdougb
2459193149Sdougb	LOCK(&lock);
2460193149Sdougb	lost = totallost;
2461193149Sdougb	for (ctx = ISC_LIST_HEAD(contexts);
2462193149Sdougb	     ctx != NULL;
2463193149Sdougb	     ctx = ISC_LIST_NEXT(ctx, link)) {
2464254402Serwin		xmlrc = renderctx(ctx, &summary, writer);
2465254402Serwin		if (xmlrc < 0) {
2466254402Serwin			UNLOCK(&lock);
2467254402Serwin			goto error;
2468254402Serwin		}
2469193149Sdougb	}
2470193149Sdougb	UNLOCK(&lock);
2471193149Sdougb
2472254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* contexts */
2473193149Sdougb
2474254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary"));
2475193149Sdougb
2476254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse"));
2477254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2478254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2479254402Serwin					    summary.total));
2480254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */
2481193149Sdougb
2482254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse"));
2483254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2484254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2485254402Serwin					    summary.inuse));
2486254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* InUse */
2487193149Sdougb
2488254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize"));
2489254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2490254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2491254402Serwin					    summary.blocksize));
2492254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* BlockSize */
2493193149Sdougb
2494254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize"));
2495254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2496254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2497254402Serwin					    summary.contextsize));
2498254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */
2499193149Sdougb
2500254402Serwin	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost"));
2501254402Serwin	TRY0(xmlTextWriterWriteFormatString(writer,
2502254402Serwin					    "%" ISC_PRINT_QUADFORMAT "u",
2503254402Serwin					    lost));
2504254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* Lost */
2505193149Sdougb
2506254402Serwin	TRY0(xmlTextWriterEndElement(writer)); /* summary */
2507254402Serwin error:
2508254402Serwin	return (xmlrc);
2509193149Sdougb}
2510193149Sdougb
2511193149Sdougb#endif /* HAVE_LIBXML2 */
2512224092Sdougb#endif /* BIND9 */
2513