mem.c 42.3 KB
Newer Older
Bob Halley's avatar
base  
Bob Halley committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1997-2003  Internet Software Consortium.
4
 *
Bob Halley's avatar
base  
Bob Halley committed
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * 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
 */

Mark Andrews's avatar
0 => 0U  
Mark Andrews committed
18
/* $Id: mem.c,v 1.128 2006/12/08 05:09:16 marka 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
Bob Halley committed
36

Bob Halley's avatar
update  
Bob Halley committed
37
#include <isc/mutex.h>
Michael Graff's avatar
Michael Graff committed
38
#include <isc/util.h>
Bob Halley's avatar
update  
Bob Halley committed
39

40 41 42
#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)

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

/*
 * Constants.
 */

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

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

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

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

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

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

103 104
#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
105

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

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

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

116
struct isc_mem {
Bob Halley's avatar
Bob Halley committed
117
	unsigned int		magic;
118
	isc_ondestroy_t		ondestroy;
119
	unsigned int		flags;
Bob Halley's avatar
Bob Halley committed
120
	isc_mutex_t		lock;
121 122 123
	isc_memalloc_t		memalloc;
	isc_memfree_t		memfree;
	void *			arg;
Bob Halley's avatar
base  
Bob Halley committed
124
	size_t			max_size;
125
	isc_boolean_t		checkfree;
Bob Halley's avatar
base  
Bob Halley committed
126
	struct stats *		stats;
127
	unsigned int		references;
128
	size_t			quota;
Bob Halley's avatar
Bob Halley committed
129
	size_t			total;
130
	size_t			inuse;
131
	size_t			maxinuse;
132 133 134 135 136
	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
137
	ISC_LIST(isc_mempool_t)	pools;
138

139
	/*  ISC_MEMFLAG_INTERNAL */
140 141 142 143 144 145 146 147 148
	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;

149
#if ISC_MEM_TRACKLINES
150
	debuglist_t *	 	debuglist;
Michael Graff's avatar
Michael Graff committed
151
#endif
152 153

	unsigned int		memalloc_failures;
154
	ISC_LINK(isc_mem_t)	link;
Michael Graff's avatar
Michael Graff committed
155 156
};

157 158
#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
159 160

struct isc_mempool {
Michael Graff's avatar
Michael Graff committed
161
	/* always unlocked */
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	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. */
178
#if ISC_MEMPOOL_NAMES
179
	char		name[16];	/*%< printed name in stats reports */
Michael Graff's avatar
Michael Graff committed
180
#endif
Bob Halley's avatar
base  
Bob Halley committed
181 182
};

183 184 185
/*
 * Private Inline-able.
 */
Bob Halley's avatar
base  
Bob Halley committed
186

187
#if ! ISC_MEM_TRACKLINES
Michael Graff's avatar
Michael Graff committed
188 189 190
#define ADD_TRACE(a, b, c, d, e)
#define DELETE_TRACE(a, b, c, d, e)
#else
191
#define ADD_TRACE(a, b, c, d, e) \
192 193 194 195 196 197
	do { \
		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
					  ISC_MEM_DEBUGRECORD)) != 0 && \
		     b != NULL) \
		         add_trace_entry(a, b, c, d, e); \
	} while (0)
Michael Graff's avatar
Michael Graff committed
198 199
#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)

200 201 202
static void
print_active(isc_mem_t *ctx, FILE *out);

203
/*!
Michael Graff's avatar
Michael Graff committed
204 205 206 207
 * 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
208
		FLARG)
Michael Graff's avatar
Michael Graff committed
209 210 211 212
{
	debuglink_t *dl;
	unsigned int i;

213
	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
214 215 216 217
		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					       ISC_MSG_ADDTRACE,
					       "add %p size %u "
					       "file %s line %u mctx %p\n"),
218
			ptr, size, file, line, mctx);
Michael Graff's avatar
Michael Graff committed
219

220
	if (mctx->debuglist == NULL)
Michael Graff's avatar
Michael Graff committed
221 222
		return;

223 224 225 226
	if (size > mctx->max_size)
		size = mctx->max_size;

	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
Michael Graff's avatar
Michael Graff committed
227 228 229
	while (dl != NULL) {
		if (dl->count == DEBUGLIST_COUNT)
			goto next;
230
		for (i = 0; i < DEBUGLIST_COUNT; i++) {
Michael Graff's avatar
Michael Graff committed
231 232
			if (dl->ptr[i] == NULL) {
				dl->ptr[i] = ptr;
233
				dl->size[i] = size;
Michael Graff's avatar
Michael Graff committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247
				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);
248
	for (i = 1; i < DEBUGLIST_COUNT; i++) {
Michael Graff's avatar
Michael Graff committed
249
		dl->ptr[i] = NULL;
250
		dl->size[i] = 0;
Michael Graff's avatar
Michael Graff committed
251 252 253 254 255
		dl->file[i] = NULL;
		dl->line[i] = 0;
	}

	dl->ptr[0] = ptr;
256
	dl->size[0] = size;
Michael Graff's avatar
Michael Graff committed
257 258 259 260
	dl->file[0] = file;
	dl->line[0] = line;
	dl->count = 1;

261
	ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);
Michael Graff's avatar
Michael Graff committed
262 263 264 265 266 267 268 269 270
}

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;

271
	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
272 273 274 275
		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					       ISC_MSG_DELTRACE,
					       "del %p size %u "
					       "file %s line %u mctx %p\n"),
276
			ptr, size, file, line, mctx);
Michael Graff's avatar
Michael Graff committed
277

278
	if (mctx->debuglist == NULL)
Michael Graff's avatar
Michael Graff committed
279 280
		return;

281 282 283 284
	if (size > mctx->max_size)
		size = mctx->max_size;

	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
Michael Graff's avatar
Michael Graff committed
285
	while (dl != NULL) {
286
		for (i = 0; i < DEBUGLIST_COUNT; i++) {
Michael Graff's avatar
Michael Graff committed
287 288
			if (dl->ptr[i] == ptr) {
				dl->ptr[i] = NULL;
289
				dl->size[i] = 0;
Michael Graff's avatar
Michael Graff committed
290 291 292 293 294 295
				dl->file[i] = NULL;
				dl->line[i] = 0;

				INSIST(dl->count > 0);
				dl->count--;
				if (dl->count == 0) {
296
					ISC_LIST_UNLINK(mctx->debuglist[size],
Michael Graff's avatar
Michael Graff committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
							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 */

314 315 316 317 318
static inline size_t
rmsize(size_t size) {
	/*
 	 * round down to ALIGNMENT_SIZE
	 */
319
	return (size & (~(ALIGNMENT_SIZE - 1)));
320 321
}

322
static inline size_t
Bob Halley's avatar
base  
Bob Halley committed
323
quantize(size_t size) {
324
	/*!
325
	 * Round up the result in order to get a size big
Bob Halley's avatar
base  
Bob Halley committed
326 327 328
	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
	 * byte boundaries.
	 */
329

Mark Andrews's avatar
Mark Andrews committed
330
	if (size == 0U)
331
		return (ALIGNMENT_SIZE);
332
	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
Bob Halley's avatar
base  
Bob Halley committed
333 334
}

335
static inline isc_boolean_t
336
more_basic_blocks(isc_mem_t *ctx) {
337 338 339 340 341
	void *new;
	unsigned char *curr, *next;
	unsigned char *first, *last;
	unsigned char **table;
	unsigned int table_size;
Bob Halley's avatar
Bob Halley committed
342
	size_t increment;
343 344 345 346
	int i;

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

347 348 349
	/*
	 * Did we hit the quota for this context?
	 */
Bob Halley's avatar
Bob Halley committed
350
	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
Mark Andrews's avatar
Mark Andrews committed
351
	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
352
		return (ISC_FALSE);
353 354 355

	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
	if (ctx->basic_table_count == ctx->basic_table_size) {
356
		table_size = ctx->basic_table_size + TABLE_INCREMENT;
357
		table = (ctx->memalloc)(ctx->arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
358
					table_size * sizeof(unsigned char *));
359 360
		if (table == NULL) {
			ctx->memalloc_failures++;
361
			return (ISC_FALSE);
362
		}
363 364 365
		if (ctx->basic_table_size != 0) {
			memcpy(table, ctx->basic_table,
			       ctx->basic_table_size *
Andreas Gustafsson's avatar
Andreas Gustafsson committed
366
			       sizeof(unsigned char *));
367
			(ctx->memfree)(ctx->arg, ctx->basic_table);
368
		}
369 370
		ctx->basic_table = table;
		ctx->basic_table_size = table_size;
371
	}
372

373
	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
374 375
	if (new == NULL) {
		ctx->memalloc_failures++;
376
		return (ISC_FALSE);
377
	}
Bob Halley's avatar
Bob Halley committed
378
	ctx->total += increment;
379 380
	ctx->basic_table[ctx->basic_table_count] = new;
	ctx->basic_table_count++;
381

382 383 384
	curr = new;
	next = curr + ctx->mem_target;
	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
385
		((element *)curr)->next = (element *)next;
386 387 388 389 390 391 392
		curr = next;
		next += ctx->mem_target;
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
Bob Halley's avatar
Bob Halley committed
393
	((element *)curr)->next = NULL;
394 395 396 397 398 399 400
	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;
401 402

	return (ISC_TRUE);
403 404
}

405 406 407 408 409 410 411
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;

412
	/*!
413 414 415 416 417 418 419 420 421 422
	 * 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.
			 */
			/*
423
			 * XXXRTH  "At quota" notification here.
424
			 */
425
			return (ISC_FALSE);
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
		}
	}

	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;
441
	total_size -= new_size;
442 443 444 445
	for (i = 0; i < (frags - 1); i++) {
		((element *)curr)->next = (element *)next;
		curr = next;
		next += new_size;
446 447 448 449 450 451
		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
452
	if (total_size > 0U) {
453 454 455
		((element *)next)->next = ctx->freelists[total_size];
		ctx->freelists[total_size] = (element *)next;
		ctx->stats[total_size].freefrags++;
456 457 458 459 460 461 462 463 464 465 466
	}
	/*
	 * 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
467
static inline void *
468
mem_getunlocked(isc_mem_t *ctx, size_t size) {
Michael Graff's avatar
Michael Graff committed
469 470
	size_t new_size = quantize(size);
	void *ret;
Bob Halley's avatar
base  
Bob Halley committed
471 472

	if (size >= ctx->max_size || new_size >= ctx->max_size) {
473 474 475
		/*
		 * memget() was called on something beyond our upper limit.
		 */
Mark Andrews's avatar
Mark Andrews committed
476
		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
Bob Halley's avatar
Bob Halley committed
477 478 479
			ret = NULL;
			goto done;
		}
Mark Andrews's avatar
Mark Andrews committed
480
		ret = (ctx->memalloc)(ctx->arg, size);
481 482 483
		if (ret == NULL) {
			ctx->memalloc_failures++;
			goto done;
Bob Halley's avatar
base  
Bob Halley committed
484
		}
485 486 487 488 489 490 491 492 493 494
		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;
495
		goto done;
Bob Halley's avatar
base  
Bob Halley committed
496 497
	}

498
	/*
Bob Halley's avatar
base  
Bob Halley committed
499 500 501 502
	 * 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.
	 */
503 504
	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
		return (NULL);
Bob Halley's avatar
base  
Bob Halley committed
505

506 507 508
	/*
	 * The free list uses the "rounded-up" size "new_size".
	 */
Bob Halley's avatar
base  
Bob Halley committed
509 510 511
	ret = ctx->freelists[new_size];
	ctx->freelists[new_size] = ctx->freelists[new_size]->next;

512
	/*
Bob Halley's avatar
base  
Bob Halley committed
513 514 515 516 517 518 519 520
	 * 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--;
521
	ctx->inuse += new_size;
Bob Halley's avatar
base  
Bob Halley committed
522 523 524

 done:

525
#if ISC_MEM_FILL
526 527
	if (ret != NULL)
		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
David Lawrence's avatar
David Lawrence committed
528 529 530
#endif

	return (ret);
Michael Graff's avatar
Michael Graff committed
531 532
}

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
#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
548
static inline void
549
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
Michael Graff's avatar
Michael Graff committed
550
	size_t new_size = quantize(size);
Bob Halley's avatar
base  
Bob Halley committed
551

552
	if (size == ctx->max_size || new_size >= ctx->max_size) {
553 554 555
		/*
		 * memput() called on something beyond our upper limit.
		 */
556
#if ISC_MEM_FILL
Mark Andrews's avatar
Mark Andrews committed
557 558
		memset(mem, 0xde, size); /* Mnemonic for "dead". */
#endif
559
		(ctx->memfree)(ctx->arg, mem);
Mark Andrews's avatar
Mark Andrews committed
560
		INSIST(ctx->stats[ctx->max_size].gets != 0U);
Bob Halley's avatar
base  
Bob Halley committed
561
		ctx->stats[ctx->max_size].gets--;
Bob Halley's avatar
Bob Halley committed
562
		INSIST(size <= ctx->total);
563
		ctx->inuse -= size;
Bob Halley's avatar
Bob Halley committed
564
		ctx->total -= size;
Michael Graff's avatar
Michael Graff committed
565
		return;
Bob Halley's avatar
base  
Bob Halley committed
566 567
	}

568 569
#if ISC_MEM_FILL
#if ISC_MEM_CHECKOVERRUN
570 571
	check_overrun(mem, size, new_size);
#endif
Mark Andrews's avatar
Mark Andrews committed
572 573
	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
#endif
574

575 576 577
	/*
	 * The free list uses the "rounded-up" size "new_size".
	 */
Mark Andrews's avatar
Mark Andrews committed
578 579
	((element *)mem)->next = ctx->freelists[new_size];
	ctx->freelists[new_size] = (element *)mem;
Bob Halley's avatar
base  
Bob Halley committed
580

581
	/*
Bob Halley's avatar
base  
Bob Halley committed
582 583 584 585 586
	 * 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
587
	INSIST(ctx->stats[size].gets != 0U);
Bob Halley's avatar
base  
Bob Halley committed
588 589
	ctx->stats[size].gets--;
	ctx->stats[new_size].freefrags++;
590
	ctx->inuse -= new_size;
Bob Halley's avatar
base  
Bob Halley committed
591 592
}

593
/*!
594 595 596 597 598 599 600 601 602 603 604
 * 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);
605 606
	if (ret == NULL)
		ctx->memalloc_failures++;	
607 608 609 610 611 612 613 614 615 616 617 618 619 620

#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);
}

621
/*!
622 623 624 625 626
 * 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
627
	INSIST(((unsigned char *)mem)[size] == 0xbe);
628 629 630
#endif
#if ISC_MEM_FILL
	memset(mem, 0xde, size); /* Mnemonic for "dead". */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
631 632
#else
	UNUSED(size);
633 634 635 636
#endif
	(ctx->memfree)(ctx->arg, mem);
}

637
/*!
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
 * 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++;
	}
}

654
/*!
655 656 657 658 659 660 661 662 663 664
 * 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) {
665
		INSIST(ctx->stats[ctx->max_size].gets > 0U);
666 667
		ctx->stats[ctx->max_size].gets--;
	} else {
668
		INSIST(ctx->stats[size].gets > 0U);
669 670 671 672
		ctx->stats[size].gets--;
	}
}

David Lawrence's avatar
David Lawrence committed
673 674 675 676 677 678 679
/*
 * Private.
 */

static void *
default_memalloc(void *arg, size_t size) {
	UNUSED(arg);
680
	if (size == 0U)
681
		size = 1;
David Lawrence's avatar
David Lawrence committed
682 683 684 685 686 687 688 689 690
	return (malloc(size));
}

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

691 692 693 694 695
static void
initialize_action(void) {
        RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
}

David Lawrence's avatar
David Lawrence committed
696 697 698 699 700 701 702 703
/*
 * 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)
704 705
{
	return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree,
706
				 arg, ctxp, ISC_MEMFLAG_DEFAULT));
707 708 709 710 711 712 713
				 
}

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
714 715
{
	isc_mem_t *ctx;
716
	isc_result_t result;
David Lawrence's avatar
David Lawrence committed
717 718 719 720 721

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

722 723
	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);

724 725
	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
726
	ctx = (memalloc)(arg, sizeof(*ctx));
David Lawrence's avatar
David Lawrence committed
727 728 729
	if (ctx == NULL)
		return (ISC_R_NOMEMORY);

730 731 732 733 734 735
	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
		result = isc_mutex_init(&ctx->lock);
		if (result != ISC_R_SUCCESS) {
			(memfree)(arg, ctx);
			return (result);
		}
736 737
	}

738
	if (init_max_size == 0U)
David Lawrence's avatar
David Lawrence committed
739 740 741
		ctx->max_size = DEF_MAX_SIZE;
	else
		ctx->max_size = init_max_size;
742
	ctx->flags = flags;
743 744 745 746
	ctx->references = 1;
	ctx->quota = 0;
	ctx->total = 0;
	ctx->inuse = 0;
747
	ctx->maxinuse = 0;
748 749 750 751 752 753 754 755 756 757 758
	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;
759
	ctx->checkfree = ISC_TRUE;
760
#if ISC_MEM_TRACKLINES
761
	ctx->debuglist = NULL;
762
#endif
763 764
	ISC_LIST_INIT(ctx->pools);
	ctx->freelists = NULL;
765 766 767 768 769 770
	ctx->basic_blocks = NULL;
	ctx->basic_table = NULL;
	ctx->basic_table_count = 0;
	ctx->basic_table_size = 0;
	ctx->lowest = NULL;
	ctx->highest = NULL;
771 772

	ctx->stats = (memalloc)(arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
773
				(ctx->max_size+1) * sizeof(struct stats));
774 775 776 777
	if (ctx->stats == NULL) {
		result = ISC_R_NOMEMORY;
		goto error;
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
778
	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
779

780
	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
Mark Andrews's avatar
Mark Andrews committed
781
		if (target_size == 0U)
782 783 784 785 786 787 788 789 790 791 792
			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
793
	}
794

795
#if ISC_MEM_TRACKLINES
796 797 798 799
	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
		unsigned int i;

		ctx->debuglist = (memalloc)(arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
800
				      (ctx->max_size+1) * sizeof(debuglist_t));
801 802 803 804 805 806 807
		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
808
#endif
David Lawrence's avatar
David Lawrence committed
809

810 811
	ctx->memalloc_failures = 0;

812 813 814 815
	LOCK(&lock);
	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
	UNLOCK(&lock);

David Lawrence's avatar
David Lawrence committed
816 817
	*ctxp = ctx;
	return (ISC_R_SUCCESS);
818 819

  error:
820 821
	if (ctx != NULL) {
		if (ctx->stats != NULL)
822
			(memfree)(arg, ctx->stats);
823
		if (ctx->freelists != NULL)
824
			(memfree)(arg, ctx->freelists);
825
#if ISC_MEM_TRACKLINES
826
		if (ctx->debuglist != NULL)
827 828
			(ctx->memfree)(ctx->arg, ctx->debuglist);
#endif /* ISC_MEM_TRACKLINES */
829 830
		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
			DESTROYLOCK(&ctx->lock);
831 832 833 834
		(memfree)(arg, ctx);
	}

	return (result);
David Lawrence's avatar
David Lawrence committed
835 836 837 838 839 840
}

isc_result_t
isc_mem_create(size_t init_max_size, size_t target_size,
	       isc_mem_t **ctxp)
{
841 842
	return (isc_mem_createx2(init_max_size, target_size,
				 default_memalloc, default_memfree, NULL,
843
				 ctxp, ISC_MEMFLAG_DEFAULT));
844 845 846 847 848 849 850 851 852
}

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
853 854 855 856 857 858 859 860 861
}

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

	ctx->magic = 0;

862 863 864 865
	LOCK(&lock);
	ISC_LIST_UNLINK(contexts, ctx, link);
	UNLOCK(&lock);

David Lawrence's avatar
David Lawrence committed
866
	INSIST(ISC_LIST_EMPTY(ctx->pools));
867

868
#if ISC_MEM_TRACKLINES
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
	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],
						 	dl, link);
					free(dl);
				}
887
		}
888
		(ctx->memfree)(ctx->arg, ctx->debuglist);
889
	}
Michael Graff's avatar
Michael Graff committed
890
#endif
David Lawrence's avatar
David Lawrence committed
891 892 893
	INSIST(ctx->references == 0);

	if (ctx->checkfree) {
894 895
		for (i = 0; i <= ctx->max_size; i++) {
#if ISC_MEM_TRACKLINES
Mark Andrews's avatar
Mark Andrews committed
896
			if (ctx->stats[i].gets != 0U)
897 898
				print_active(ctx, stderr);
#endif
899
			INSIST(ctx->stats[i].gets == 0U);
900
		}
David Lawrence's avatar
David Lawrence committed
901 902
	}

903
	(ctx->memfree)(ctx->arg, ctx->stats);
David Lawrence's avatar
David Lawrence committed
904

905 906 907 908 909 910
	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
		for (i = 0; i < ctx->basic_table_count; i++)
			(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
		(ctx->memfree)(ctx->arg, ctx->freelists);
		(ctx->memfree)(ctx->arg, ctx->basic_table);
	}
David Lawrence's avatar
David Lawrence committed
911 912 913

	ondest = ctx->ondestroy;

914 915
	if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
		DESTROYLOCK(&ctx->lock);
David Lawrence's avatar
David Lawrence committed
916 917 918 919 920 921 922 923 924 925
	(ctx->memfree)(ctx->arg, ctx);

	isc_ondestroy_notify(&ondest, ctx);
}

void
isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
	REQUIRE(VALID_CONTEXT(source));
	REQUIRE(targetp != NULL && *targetp == NULL);

926
	MCTXLOCK(source, &source->lock);
David Lawrence's avatar
David Lawrence committed
927
	source->references++;
928
	MCTXUNLOCK(source, &source->lock);
David Lawrence's avatar
David Lawrence committed
929 930 931 932 933 934 935 936 937 938 939 940 941

	*targetp = source;
}

void
isc_mem_detach(isc_mem_t **ctxp) {
	isc_mem_t *ctx;
	isc_boolean_t want_destroy = ISC_FALSE;

	REQUIRE(ctxp != NULL);
	ctx = *ctxp;
	REQUIRE(VALID_CONTEXT(ctx));

942
	MCTXLOCK(ctx, &ctx->lock);
David Lawrence's avatar
David Lawrence committed
943 944 945 946
	INSIST(ctx->references > 0);
	ctx->references--;
	if (ctx->references == 0)
		want_destroy = ISC_TRUE;
947
	MCTXUNLOCK(ctx, &ctx->lock);
David Lawrence's avatar
David Lawrence committed
948 949 950 951 952 953 954

	if (want_destroy)
		destroy(ctx);

	*ctxp = NULL;
}

955 956 957 958 959 960 961 962 963 964 965 966 967 968
/*
 * isc_mem_putanddetach() is the equivalent of:
 *
 * mctx = NULL;
 * isc_mem_attach(ptr->mctx, &mctx);
 * isc_mem_detach(&ptr->mctx);
 * isc_mem_put(mctx, ptr, sizeof(*ptr);
 * isc_mem_detach(&mctx);
 */

void
isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
	isc_mem_t *ctx;
	isc_boolean_t want_destroy = ISC_FALSE;
969 970
	size_info *si;
	size_t oldsize;
971 972 973 974 975 976 977 978 979 980 981 982