mem.c 49.6 KB
Newer Older
Bob Halley's avatar
base  
Bob Halley committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1997-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
Bob Halley's avatar
base  
Bob Halley committed
6 7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
base  
Bob Halley committed
16 17
 */

18
/* $Id: mem.c,v 1.148 2009/02/11 03:04:18 jinmei Exp $ */
19 20

/*! \file */
David Lawrence's avatar
David Lawrence committed
21

Bob Halley's avatar
Bob Halley committed
22
#include <config.h>
Bob Halley's avatar
base  
Bob Halley committed
23 24 25

#include <stdio.h>
#include <stdlib.h>
Bob Halley's avatar
Bob Halley committed
26
#include <stddef.h>
Bob Halley's avatar
base  
Bob Halley committed
27

Michael Graff's avatar
Michael Graff committed
28 29
#include <limits.h>

30
#include <isc/magic.h>
Bob Halley's avatar
Bob Halley committed
31
#include <isc/mem.h>
32
#include <isc/msgs.h>
33
#include <isc/once.h>
34
#include <isc/ondestroy.h>
35
#include <isc/string.h>
Bob Halley's avatar
update  
Bob Halley committed
36
#include <isc/mutex.h>
37
#include <isc/print.h>
Michael Graff's avatar
Michael Graff committed
38
#include <isc/util.h>
39
#include <isc/xml.h>
Bob Halley's avatar
update  
Bob Halley committed
40

41 42 43
#define MCTXLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) LOCK(l)
#define MCTXUNLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) UNLOCK(l)

44 45 46
#ifndef ISC_MEM_DEBUGGING
#define ISC_MEM_DEBUGGING 0
#endif
47
LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
48 49 50 51 52 53 54

/*
 * Constants.
 */

#define DEF_MAX_SIZE		1100
#define DEF_MEM_TARGET		4096
55 56
#define ALIGNMENT_SIZE		8		/*%< must be a power of 2 */
#define NUM_BASIC_BLOCKS	64		/*%< must be > 1 */
57
#define TABLE_INCREMENT		1024
Michael Graff's avatar
Michael Graff committed
58
#define DEBUGLIST_COUNT		1024
59

Bob Halley's avatar
base  
Bob Halley committed
60 61 62
/*
 * Types.
 */
63
#if ISC_MEM_TRACKLINES
Michael Graff's avatar
Michael Graff committed
64 65 66 67
typedef struct debuglink debuglink_t;
struct debuglink {
	ISC_LINK(debuglink_t)	link;
	const void	       *ptr[DEBUGLIST_COUNT];
68
	unsigned int		size[DEBUGLIST_COUNT];
Michael Graff's avatar
Michael Graff committed
69 70 71 72
	const char	       *file[DEBUGLIST_COUNT];
	unsigned int		line[DEBUGLIST_COUNT];
	unsigned int		count;
};
Bob Halley's avatar
base  
Bob Halley committed
73

Michael Graff's avatar
Michael Graff committed
74 75 76 77 78 79
#define FLARG_PASS	, file, line
#define FLARG		, const char *file, int line
#else
#define FLARG_PASS
#define FLARG
#endif
80

Michael Graff's avatar
Michael Graff committed
81
typedef struct element element;
82 83 84
struct element {
	element *		next;
};
Bob Halley's avatar
base  
Bob Halley committed
85 86

typedef struct {
87
	/*!
Bob Halley's avatar
base  
Bob Halley committed
88 89
	 * This structure must be ALIGNMENT_SIZE bytes.
	 */
90 91
	union {
		size_t		size;
92
		isc_mem_t	*ctx;
93 94
		char		bytes[ALIGNMENT_SIZE];
	} u;
Bob Halley's avatar
Bob Halley committed
95
} size_info;
Bob Halley's avatar
base  
Bob Halley committed
96 97

struct stats {
Bob Halley's avatar
Bob Halley committed
98 99 100 101
	unsigned long		gets;
	unsigned long		totalgets;
	unsigned long		blocks;
	unsigned long		freefrags;
Bob Halley's avatar
base  
Bob Halley committed
102 103
};

104 105
#define MEM_MAGIC		ISC_MAGIC('M', 'e', 'm', 'C')
#define VALID_CONTEXT(c)	ISC_MAGIC_VALID(c, MEM_MAGIC)
Bob Halley's avatar
Bob Halley committed
106

107
#if ISC_MEM_TRACKLINES
108
typedef ISC_LIST(debuglink_t)	debuglist_t;
109
#endif
110

111 112 113 114
/* List of all active memory contexts. */

static ISC_LIST(isc_mem_t)	contexts;
static isc_once_t		once = ISC_ONCE_INIT;
115
static isc_mutex_t		lock;
116

117 118 119 120 121 122
/*%
 * Total size of lost memory due to a bug of external library.
 * Locked by the global lock.
 */
static isc_uint64_t		totallost;

123
struct isc_mem {
Bob Halley's avatar
Bob Halley committed
124
	unsigned int		magic;
125
	isc_ondestroy_t		ondestroy;
126
	unsigned int		flags;
127
	isc_mutex_t		lock;
128 129 130
	isc_memalloc_t		memalloc;
	isc_memfree_t		memfree;
	void *			arg;
Bob Halley's avatar
base  
Bob Halley committed
131
	size_t			max_size;
132
	isc_boolean_t		checkfree;
Bob Halley's avatar
base  
Bob Halley committed
133
	struct stats *		stats;
134
	unsigned int		references;
135 136
	char			name[16];
	void *			tag;
137
	size_t			quota;
Bob Halley's avatar
Bob Halley committed
138
	size_t			total;
139
	size_t			inuse;
140
	size_t			maxinuse;
141 142 143 144 145
	size_t			hi_water;
	size_t			lo_water;
	isc_boolean_t		hi_called;
	isc_mem_water_t		water;
	void *			water_arg;
Michael Graff's avatar
Michael Graff committed
146
	ISC_LIST(isc_mempool_t)	pools;
147
	unsigned int		poolcnt;
148

149
	/*  ISC_MEMFLAG_INTERNAL */
150 151 152 153 154 155 156 157 158
	size_t			mem_target;
	element **		freelists;
	element *		basic_blocks;
	unsigned char **	basic_table;
	unsigned int		basic_table_count;
	unsigned int		basic_table_size;
	unsigned char *		lowest;
	unsigned char *		highest;

159
#if ISC_MEM_TRACKLINES
160
	debuglist_t *	 	debuglist;
161
	unsigned int		debuglistcnt;
Michael Graff's avatar
Michael Graff committed
162
#endif
163 164

	unsigned int		memalloc_failures;
165
	ISC_LINK(isc_mem_t)	link;
Michael Graff's avatar
Michael Graff committed
166 167
};

168 169
#define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
#define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
Michael Graff's avatar
Michael Graff committed
170 171

struct isc_mempool {
Michael Graff's avatar
Michael Graff committed
172
	/* always unlocked */
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
	unsigned int	magic;		/*%< magic number */
	isc_mutex_t    *lock;		/*%< optional lock */
	isc_mem_t      *mctx;		/*%< our memory context */
	/*%< locked via the memory context's lock */
	ISC_LINK(isc_mempool_t)	link;	/*%< next pool in this mem context */
	/*%< optionally locked from here down */
	element	       *items;		/*%< low water item list */
	size_t		size;		/*%< size of each item on this pool */
	unsigned int	maxalloc;	/*%< max number of items allowed */
	unsigned int	allocated;	/*%< # of items currently given out */
	unsigned int	freecount;	/*%< # of items on reserved list */
	unsigned int	freemax;	/*%< # of items allowed on free list */
	unsigned int	fillcount;	/*%< # of items to fetch on each fill */
	/*%< Stats only. */
	unsigned int	gets;		/*%< # of requests to this pool */
	/*%< Debugging only. */
189
#if ISC_MEMPOOL_NAMES
190
	char		name[16];	/*%< printed name in stats reports */
Michael Graff's avatar
Michael Graff committed
191
#endif
Bob Halley's avatar
base  
Bob Halley committed
192 193
};

194 195 196
/*
 * Private Inline-able.
 */
Bob Halley's avatar
base  
Bob Halley committed
197

198
#if ! ISC_MEM_TRACKLINES
Michael Graff's avatar
Michael Graff committed
199 200 201
#define ADD_TRACE(a, b, c, d, e)
#define DELETE_TRACE(a, b, c, d, e)
#else
202
#define ADD_TRACE(a, b, c, d, e) \
203 204 205 206
	do { \
		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
					  ISC_MEM_DEBUGRECORD)) != 0 && \
		     b != NULL) \
Automatic Updater's avatar
Automatic Updater committed
207
			 add_trace_entry(a, b, c, d, e); \
208
	} while (0)
Michael Graff's avatar
Michael Graff committed
209 210
#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)

211 212 213
static void
print_active(isc_mem_t *ctx, FILE *out);

214
/*!
Michael Graff's avatar
Michael Graff committed
215 216 217 218
 * mctx must be locked.
 */
static inline void
add_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size
Michael Graff's avatar
pasto  
Michael Graff committed
219
		FLARG)
Michael Graff's avatar
Michael Graff committed
220 221 222 223
{
	debuglink_t *dl;
	unsigned int i;

224
	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
225 226 227 228
		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					       ISC_MSG_ADDTRACE,
					       "add %p size %u "
					       "file %s line %u mctx %p\n"),
229
			ptr, size, file, line, mctx);
Michael Graff's avatar
Michael Graff committed
230

231
	if (mctx->debuglist == NULL)
Michael Graff's avatar
Michael Graff committed
232 233
		return;

234 235 236 237
	if (size > mctx->max_size)
		size = mctx->max_size;

	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
Michael Graff's avatar
Michael Graff committed
238 239 240
	while (dl != NULL) {
		if (dl->count == DEBUGLIST_COUNT)
			goto next;
241
		for (i = 0; i < DEBUGLIST_COUNT; i++) {
Michael Graff's avatar
Michael Graff committed
242 243
			if (dl->ptr[i] == NULL) {
				dl->ptr[i] = ptr;
244
				dl->size[i] = size;
Michael Graff's avatar
Michael Graff committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258
				dl->file[i] = file;
				dl->line[i] = line;
				dl->count++;
				return;
			}
		}
	next:
		dl = ISC_LIST_NEXT(dl, link);
	}

	dl = malloc(sizeof(debuglink_t));
	INSIST(dl != NULL);

	ISC_LINK_INIT(dl, link);
259
	for (i = 1; i < DEBUGLIST_COUNT; i++) {
Michael Graff's avatar
Michael Graff committed
260
		dl->ptr[i] = NULL;
261
		dl->size[i] = 0;
Michael Graff's avatar
Michael Graff committed
262 263 264 265 266
		dl->file[i] = NULL;
		dl->line[i] = 0;
	}

	dl->ptr[0] = ptr;
267
	dl->size[0] = size;
Michael Graff's avatar
Michael Graff committed
268 269 270 271
	dl->file[0] = file;
	dl->line[0] = line;
	dl->count = 1;

272
	ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);
273
	mctx->debuglistcnt++;
Michael Graff's avatar
Michael Graff committed
274 275 276 277 278 279 280 281 282
}

static inline void
delete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size,
		   const char *file, unsigned int line)
{
	debuglink_t *dl;
	unsigned int i;

283
	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
284 285 286 287
		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					       ISC_MSG_DELTRACE,
					       "del %p size %u "
					       "file %s line %u mctx %p\n"),
288
			ptr, size, file, line, mctx);
Michael Graff's avatar
Michael Graff committed
289

290
	if (mctx->debuglist == NULL)
Michael Graff's avatar
Michael Graff committed
291 292
		return;

293 294 295 296
	if (size > mctx->max_size)
		size = mctx->max_size;

	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
Michael Graff's avatar
Michael Graff committed
297
	while (dl != NULL) {
298
		for (i = 0; i < DEBUGLIST_COUNT; i++) {
Michael Graff's avatar
Michael Graff committed
299 300
			if (dl->ptr[i] == ptr) {
				dl->ptr[i] = NULL;
301
				dl->size[i] = 0;
Michael Graff's avatar
Michael Graff committed
302 303 304 305 306 307
				dl->file[i] = NULL;
				dl->line[i] = 0;

				INSIST(dl->count > 0);
				dl->count--;
				if (dl->count == 0) {
308
					ISC_LIST_UNLINK(mctx->debuglist[size],
Michael Graff's avatar
Michael Graff committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
							dl, link);
					free(dl);
				}
				return;
			}
		}
		dl = ISC_LIST_NEXT(dl, link);
	}

	/*
	 * If we get here, we didn't find the item on the list.  We're
	 * screwed.
	 */
	INSIST(dl != NULL);
}
#endif /* ISC_MEM_TRACKLINES */

326 327 328
static inline size_t
rmsize(size_t size) {
	/*
Automatic Updater's avatar
Automatic Updater committed
329
	 * round down to ALIGNMENT_SIZE
330
	 */
331
	return (size & (~(ALIGNMENT_SIZE - 1)));
332 333
}

334
static inline size_t
Bob Halley's avatar
base  
Bob Halley committed
335
quantize(size_t size) {
336
	/*!
337
	 * Round up the result in order to get a size big
Bob Halley's avatar
base  
Bob Halley committed
338 339 340
	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
	 * byte boundaries.
	 */
341

Mark Andrews's avatar
Mark Andrews committed
342
	if (size == 0U)
343
		return (ALIGNMENT_SIZE);
344
	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
Bob Halley's avatar
base  
Bob Halley committed
345 346
}

347
static inline isc_boolean_t
348
more_basic_blocks(isc_mem_t *ctx) {
349 350 351 352 353
	void *new;
	unsigned char *curr, *next;
	unsigned char *first, *last;
	unsigned char **table;
	unsigned int table_size;
Bob Halley's avatar
Bob Halley committed
354
	size_t increment;
355 356 357 358
	int i;

	/* Require: we hold the context lock. */

359 360 361
	/*
	 * Did we hit the quota for this context?
	 */
Bob Halley's avatar
Bob Halley committed
362
	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
Mark Andrews's avatar
Mark Andrews committed
363
	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
364
		return (ISC_FALSE);
365 366 367

	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
	if (ctx->basic_table_count == ctx->basic_table_size) {
368
		table_size = ctx->basic_table_size + TABLE_INCREMENT;
369
		table = (ctx->memalloc)(ctx->arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
370
					table_size * sizeof(unsigned char *));
371 372
		if (table == NULL) {
			ctx->memalloc_failures++;
373
			return (ISC_FALSE);
374
		}
375 376 377
		if (ctx->basic_table_size != 0) {
			memcpy(table, ctx->basic_table,
			       ctx->basic_table_size *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
378
			       sizeof(unsigned char *));
379
			(ctx->memfree)(ctx->arg, ctx->basic_table);
380
		}
381 382
		ctx->basic_table = table;
		ctx->basic_table_size = table_size;
383
	}
384

385
	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
386 387
	if (new == NULL) {
		ctx->memalloc_failures++;
388
		return (ISC_FALSE);
389
	}
Bob Halley's avatar
Bob Halley committed
390
	ctx->total += increment;
391 392
	ctx->basic_table[ctx->basic_table_count] = new;
	ctx->basic_table_count++;
393

394 395 396
	curr = new;
	next = curr + ctx->mem_target;
	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
397
		((element *)curr)->next = (element *)next;
398 399 400 401 402 403 404
		curr = next;
		next += ctx->mem_target;
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
Bob Halley's avatar
Bob Halley committed
405
	((element *)curr)->next = NULL;
406 407 408 409 410 411 412
	first = new;
	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
	if (first < ctx->lowest || ctx->lowest == NULL)
		ctx->lowest = first;
	if (last > ctx->highest)
		ctx->highest = last;
	ctx->basic_blocks = new;
413 414

	return (ISC_TRUE);
415 416
}

417 418 419 420 421 422 423
static inline isc_boolean_t
more_frags(isc_mem_t *ctx, size_t new_size) {
	int i, frags;
	size_t total_size;
	void *new;
	unsigned char *curr, *next;

424
	/*!
425 426 427 428 429 430 431 432 433 434
	 * Try to get more fragments by chopping up a basic block.
	 */

	if (ctx->basic_blocks == NULL) {
		if (!more_basic_blocks(ctx)) {
			/*
			 * We can't get more memory from the OS, or we've
			 * hit the quota for this context.
			 */
			/*
435
			 * XXXRTH  "At quota" notification here.
436
			 */
437
			return (ISC_FALSE);
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
		}
	}

	total_size = ctx->mem_target;
	new = ctx->basic_blocks;
	ctx->basic_blocks = ctx->basic_blocks->next;
	frags = total_size / new_size;
	ctx->stats[new_size].blocks++;
	ctx->stats[new_size].freefrags += frags;
	/*
	 * Set up a linked-list of blocks of size
	 * "new_size".
	 */
	curr = new;
	next = curr + new_size;
453
	total_size -= new_size;
454 455 456 457
	for (i = 0; i < (frags - 1); i++) {
		((element *)curr)->next = (element *)next;
		curr = next;
		next += new_size;
458 459 460 461 462 463
		total_size -= new_size;
	}
	/*
	 * Add the remaining fragment of the basic block to a free list.
	 */
	total_size = rmsize(total_size);
Mark Andrews's avatar
Mark Andrews committed
464
	if (total_size > 0U) {
465 466 467
		((element *)next)->next = ctx->freelists[total_size];
		ctx->freelists[total_size] = (element *)next;
		ctx->stats[total_size].freefrags++;
468 469 470 471 472 473 474 475 476 477 478
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
	((element *)curr)->next = NULL;
	ctx->freelists[new_size] = new;

	return (ISC_TRUE);
}

Michael Graff's avatar
Michael Graff committed
479
static inline void *
480
mem_getunlocked(isc_mem_t *ctx, size_t size) {
Michael Graff's avatar
Michael Graff committed
481 482
	size_t new_size = quantize(size);
	void *ret;
Bob Halley's avatar
base  
Bob Halley committed
483 484

	if (size >= ctx->max_size || new_size >= ctx->max_size) {
485 486 487
		/*
		 * memget() was called on something beyond our upper limit.
		 */
Mark Andrews's avatar
Mark Andrews committed
488
		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
Bob Halley's avatar
Bob Halley committed
489 490 491
			ret = NULL;
			goto done;
		}
Mark Andrews's avatar
Mark Andrews committed
492
		ret = (ctx->memalloc)(ctx->arg, size);
493 494 495
		if (ret == NULL) {
			ctx->memalloc_failures++;
			goto done;
Bob Halley's avatar
base  
Bob Halley committed
496
		}
497 498 499 500 501 502 503 504 505 506
		ctx->total += size;
		ctx->inuse += size;
		ctx->stats[ctx->max_size].gets++;
		ctx->stats[ctx->max_size].totalgets++;
		/*
		 * If we don't set new_size to size, then the
		 * ISC_MEM_FILL code might write over bytes we
		 * don't own.
		 */
		new_size = size;
507
		goto done;
Bob Halley's avatar
base  
Bob Halley committed
508 509
	}

510
	/*
Bob Halley's avatar
base  
Bob Halley committed
511 512 513 514
	 * If there are no blocks in the free list for this size, get a chunk
	 * of memory and then break it up into "new_size"-sized blocks, adding
	 * them to the free list.
	 */
515 516
	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
		return (NULL);
Bob Halley's avatar
base  
Bob Halley committed
517

518 519 520
	/*
	 * The free list uses the "rounded-up" size "new_size".
	 */
Bob Halley's avatar
base  
Bob Halley committed
521 522 523
	ret = ctx->freelists[new_size];
	ctx->freelists[new_size] = ctx->freelists[new_size]->next;

524
	/*
Bob Halley's avatar
base  
Bob Halley committed
525 526 527 528 529 530 531 532
	 * The stats[] uses the _actual_ "size" requested by the
	 * caller, with the caveat (in the code above) that "size" >= the
	 * max. size (max_size) ends up getting recorded as a call to
	 * max_size.
	 */
	ctx->stats[size].gets++;
	ctx->stats[size].totalgets++;
	ctx->stats[new_size].freefrags--;
533
	ctx->inuse += new_size;
Bob Halley's avatar
base  
Bob Halley committed
534 535 536

 done:

537
#if ISC_MEM_FILL
538 539
	if (ret != NULL)
		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
David Lawrence's avatar
David Lawrence committed
540 541 542
#endif

	return (ret);
Michael Graff's avatar
Michael Graff committed
543 544
}

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
static inline void
check_overrun(void *mem, size_t size, size_t new_size) {
	unsigned char *cp;

	cp = (unsigned char *)mem;
	cp += size;
	while (size < new_size) {
		INSIST(*cp == 0xbe);
		cp++;
		size++;
	}
}
#endif

Michael Graff's avatar
Michael Graff committed
560
static inline void
561
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
Michael Graff's avatar
Michael Graff committed
562
	size_t new_size = quantize(size);
Bob Halley's avatar
base  
Bob Halley committed
563

564
	if (size == ctx->max_size || new_size >= ctx->max_size) {
565 566 567
		/*
		 * memput() called on something beyond our upper limit.
		 */
568
#if ISC_MEM_FILL
Mark Andrews's avatar
Mark Andrews committed
569 570
		memset(mem, 0xde, size); /* Mnemonic for "dead". */
#endif
571
		(ctx->memfree)(ctx->arg, mem);
Mark Andrews's avatar
Mark Andrews committed
572
		INSIST(ctx->stats[ctx->max_size].gets != 0U);
Bob Halley's avatar
base  
Bob Halley committed
573
		ctx->stats[ctx->max_size].gets--;
Bob Halley's avatar
Bob Halley committed
574
		INSIST(size <= ctx->total);
575
		ctx->inuse -= size;
Bob Halley's avatar
Bob Halley committed
576
		ctx->total -= size;
Michael Graff's avatar
Michael Graff committed
577
		return;
Bob Halley's avatar
base  
Bob Halley committed
578 579
	}

580 581
#if ISC_MEM_FILL
#if ISC_MEM_CHECKOVERRUN
582 583
	check_overrun(mem, size, new_size);
#endif
Mark Andrews's avatar
Mark Andrews committed
584 585
	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
#endif
586

587 588 589
	/*
	 * The free list uses the "rounded-up" size "new_size".
	 */
Mark Andrews's avatar
Mark Andrews committed
590 591
	((element *)mem)->next = ctx->freelists[new_size];
	ctx->freelists[new_size] = (element *)mem;
Bob Halley's avatar
base  
Bob Halley committed
592

593
	/*
Bob Halley's avatar
base  
Bob Halley committed
594 595 596 597 598
	 * The stats[] uses the _actual_ "size" requested by the
	 * caller, with the caveat (in the code above) that "size" >= the
	 * max. size (max_size) ends up getting recorded as a call to
	 * max_size.
	 */
Mark Andrews's avatar
Mark Andrews committed
599
	INSIST(ctx->stats[size].gets != 0U);
Bob Halley's avatar
base  
Bob Halley committed
600 601
	ctx->stats[size].gets--;
	ctx->stats[new_size].freefrags++;
602
	ctx->inuse -= new_size;
Bob Halley's avatar
base  
Bob Halley committed
603 604
}

605
/*!
606 607 608 609 610 611 612 613 614 615 616
 * Perform a malloc, doing memory filling and overrun detection as necessary.
 */
static inline void *
mem_get(isc_mem_t *ctx, size_t size) {
	char *ret;

#if ISC_MEM_CHECKOVERRUN
	size += 1;
#endif

	ret = (ctx->memalloc)(ctx->arg, size);
617
	if (ret == NULL)
Automatic Updater's avatar
Automatic Updater committed
618
		ctx->memalloc_failures++;
619 620 621 622 623 624 625 626 627 628 629 630 631 632

#if ISC_MEM_FILL
	if (ret != NULL)
		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
#else
#  if ISC_MEM_CHECKOVERRUN
	if (ret != NULL)
		ret[size-1] = 0xbe;
#  endif
#endif

	return (ret);
}

633
/*!
634 635 636 637 638
 * Perform a free, doing memory filling and overrun detection as necessary.
 */
static inline void
mem_put(isc_mem_t *ctx, void *mem, size_t size) {
#if ISC_MEM_CHECKOVERRUN
639
	INSIST(((unsigned char *)mem)[size] == 0xbe);
640 641 642
#endif
#if ISC_MEM_FILL
	memset(mem, 0xde, size); /* Mnemonic for "dead". */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
643 644
#else
	UNUSED(size);
645 646 647 648
#endif
	(ctx->memfree)(ctx->arg, mem);
}

649
/*!
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
 * Update internal counters after a memory get.
 */
static inline void
mem_getstats(isc_mem_t *ctx, size_t size) {
	ctx->total += size;
	ctx->inuse += size;

	if (size > ctx->max_size) {
		ctx->stats[ctx->max_size].gets++;
		ctx->stats[ctx->max_size].totalgets++;
	} else {
		ctx->stats[size].gets++;
		ctx->stats[size].totalgets++;
	}
}

666
/*!
667 668 669 670 671 672 673 674 675 676
 * Update internal counters after a memory put.
 */
static inline void
mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
	UNUSED(ptr);

	INSIST(ctx->inuse >= size);
	ctx->inuse -= size;

	if (size > ctx->max_size) {
677
		INSIST(ctx->stats[ctx->max_size].gets > 0U);
678 679
		ctx->stats[ctx->max_size].gets--;
	} else {
680
		INSIST(ctx->stats[size].gets > 0U);
681 682 683 684
		ctx->stats[size].gets--;
	}
}

David Lawrence's avatar
David Lawrence committed
685 686 687 688 689 690 691
/*
 * Private.
 */

static void *
default_memalloc(void *arg, size_t size) {
	UNUSED(arg);
692
	if (size == 0U)
693
		size = 1;
David Lawrence's avatar
David Lawrence committed
694 695 696 697 698 699 700 701 702
	return (malloc(size));
}

static void
default_memfree(void *arg, void *ptr) {
	UNUSED(arg);
	free(ptr);
}

703 704
static void
initialize_action(void) {
705
	RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
Mark Andrews's avatar
Mark Andrews committed
706
	ISC_LIST_INIT(contexts);
707
	totallost = 0;
708 709
}

David Lawrence's avatar
David Lawrence committed
710 711 712 713 714 715 716 717
/*
 * Public.
 */

isc_result_t
isc_mem_createx(size_t init_max_size, size_t target_size,
		isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
		isc_mem_t **ctxp)
718 719
{
	return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree,
720
				 arg, ctxp, ISC_MEMFLAG_DEFAULT));
Automatic Updater's avatar
Automatic Updater committed
721

722 723 724 725 726 727
}

isc_result_t
isc_mem_createx2(size_t init_max_size, size_t target_size,
		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
		 isc_mem_t **ctxp, unsigned int flags)
David Lawrence's avatar
David Lawrence committed
728 729
{
	isc_mem_t *ctx;
730
	isc_result_t result;
David Lawrence's avatar
David Lawrence committed
731 732 733 734 735

	REQUIRE(ctxp != NULL && *ctxp == NULL);
	REQUIRE(memalloc != NULL);
	REQUIRE(memfree != NULL);

736 737
	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);

738 739
	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
740
	ctx = (memalloc)(arg, sizeof(*ctx));
David Lawrence's avatar
David Lawrence committed
741 742 743
	if (ctx == NULL)
		return (ISC_R_NOMEMORY);

744
	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
745
		result = isc_mutex_init(&ctx->lock);
746 747 748 749
		if (result != ISC_R_SUCCESS) {
			(memfree)(arg, ctx);
			return (result);
		}
750 751
	}

752
	if (init_max_size == 0U)
David Lawrence's avatar
David Lawrence committed
753 754 755
		ctx->max_size = DEF_MAX_SIZE;
	else
		ctx->max_size = init_max_size;
756
	ctx->flags = flags;
757
	ctx->references = 1;
758 759
	memset(ctx->name, 0, sizeof(ctx->name));
	ctx->tag = NULL;
760 761 762
	ctx->quota = 0;
	ctx->total = 0;
	ctx->inuse = 0;
763
	ctx->maxinuse = 0;
764 765 766 767 768 769 770 771 772 773 774
	ctx->hi_water = 0;
	ctx->lo_water = 0;
	ctx->hi_called = ISC_FALSE;
	ctx->water = NULL;
	ctx->water_arg = NULL;
	ctx->magic = MEM_MAGIC;
	isc_ondestroy_init(&ctx->ondestroy);
	ctx->memalloc = memalloc;
	ctx->memfree = memfree;
	ctx->arg = arg;
	ctx->stats = NULL;
775
	ctx->checkfree = ISC_TRUE;
776
#if ISC_MEM_TRACKLINES
777
	ctx->debuglist = NULL;
778
	ctx->debuglistcnt = 0;
779
#endif
780
	ISC_LIST_INIT(ctx->pools);
781
	ctx->poolcnt = 0;
782
	ctx->freelists = NULL;
783 784 785 786 787 788
	ctx->basic_blocks = NULL;
	ctx->basic_table = NULL;
	ctx->basic_table_count = 0;
	ctx->basic_table_size = 0;
	ctx->lowest = NULL;
	ctx->highest = NULL;
789 790

	ctx->stats = (memalloc)(arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
791
				(ctx->max_size+1) * sizeof(struct stats));
792 793 794 795
	if (ctx->stats == NULL) {
		result = ISC_R_NOMEMORY;
		goto error;
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
796
	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
797

798
	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
Mark Andrews's avatar
Mark Andrews committed
799
		if (target_size == 0U)
800 801 802 803 804 805 806 807 808 809 810
			ctx->mem_target = DEF_MEM_TARGET;
		else
			ctx->mem_target = target_size;
		ctx->freelists = (memalloc)(arg, ctx->max_size *
						 sizeof(element *));
		if (ctx->freelists == NULL) {
			result = ISC_R_NOMEMORY;
			goto error;
		}
		memset(ctx->freelists, 0,
		       ctx->max_size * sizeof(element *));
David Lawrence's avatar
David Lawrence committed
811
	}
812

813
#if ISC_MEM_TRACKLINES
814 815 816 817
	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
		unsigned int i;

		ctx->debuglist = (memalloc)(arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
818
				      (ctx->max_size+1) * sizeof(debuglist_t));
819 820 821 822 823 824 825
		if (ctx->debuglist == NULL) {
			result = ISC_R_NOMEMORY;
			goto error;
		}
		for (i = 0; i <= ctx->max_size; i++)
			ISC_LIST_INIT(ctx->debuglist[i]);
	}
Michael Graff's avatar
Michael Graff committed
826
#endif
David Lawrence's avatar
David Lawrence committed
827

828 829
	ctx->memalloc_failures = 0;

830
	LOCK(&lock);
831
	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
832
	UNLOCK(&lock);
833

David Lawrence's avatar
David Lawrence committed
834 835
	*ctxp = ctx;
	return (ISC_R_SUCCESS);
836 837

  error:
838 839
	if (ctx != NULL) {
		if (ctx->stats != NULL)
840
			(memfree)(arg, ctx->stats);
841
		if (ctx->freelists != NULL)
842
			(memfree)(arg, ctx->freelists);
843
#if ISC_MEM_TRACKLINES
844
		if (ctx->debuglist != NULL)
845 846
			(ctx->memfree)(ctx->arg, ctx->debuglist);
#endif /* ISC_MEM_TRACKLINES */
847
		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
848
			DESTROYLOCK(&ctx->lock);
849 850 851 852
		(memfree)(arg, ctx);
	}

	return (result);
David Lawrence's avatar
David Lawrence committed
853 854 855 856 857 858
}

isc_result_t
isc_mem_create(size_t init_max_size, size_t target_size,
	       isc_mem_t **ctxp)
{
859 860
	return (isc_mem_createx2(init_max_size, target_size,
				 default_memalloc, default_memfree, NULL,
861
				 ctxp, ISC_MEMFLAG_DEFAULT));
862 863 864 865 866 867 868 869 870
}

isc_result_t
isc_mem_create2(size_t init_max_size, size_t target_size,
		isc_mem_t **ctxp, unsigned int flags)
{
	return (isc_mem_createx2(init_max_size, target_size,
				 default_memalloc, default_memfree, NULL,
				 ctxp, flags));
David Lawrence's avatar
David Lawrence committed
871 872 873 874 875 876 877 878 879
}

static void
destroy(isc_mem_t *ctx) {
	unsigned int i;
	isc_ondestroy_t ondest;

	ctx->magic = 0;

880
	LOCK(&lock);
881
	ISC_LIST_UNLINK(contexts, ctx, link);
882
	totallost += ctx->inuse;
883
	UNLOCK(&lock);
884

David Lawrence's avatar
David Lawrence committed
885
	INSIST(ISC_LIST_EMPTY(ctx->pools));
886

887
#if ISC_MEM_TRACKLINES
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
	if (ctx->debuglist != NULL) {
		if (ctx->checkfree) {
			for (i = 0; i <= ctx->max_size; i++) {
				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
					print_active(ctx, stderr);
				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
			}
		} else {
			debuglink_t *dl;

			for (i = 0; i <= ctx->max_size; i++)
				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
				     dl != NULL;
				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
					ISC_LIST_UNLINK(ctx->debuglist[i],
Automatic Updater's avatar
Automatic Updater committed
903
							dl, link);
904 905
					free(dl);
				}
906
		}
907
		(ctx->memfree)(ctx->arg, ctx->debuglist);
908
	}
Michael Graff's avatar
Michael Graff committed
909
#endif
David Lawrence's avatar
David Lawrence committed
910 911 912
	INSIST(ctx->references == 0);

	if (ctx->checkfree) {
913 914
		for (i = 0; i <= ctx->max_size; i++) {
#if ISC_MEM_TRACKLINES
Mark Andrews's avatar
Mark Andrews committed
915
			if (ctx->stats[i].gets != 0U)
916 917
				print_active(ctx, stderr);
#endif
918
			INSIST(ctx->stats[i].gets == 0U);