mem.c 21.4 KB
Newer Older
Bob Halley's avatar
base  
Bob Halley committed
1
/*
Bob Halley's avatar
Bob Halley committed
2
 * Copyright (C) 1997, 1998, 1999  Internet Software Consortium.
Bob Halley's avatar
Bob Halley committed
3
 * 
Bob Halley's avatar
base  
Bob Halley committed
4 5 6
 * 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.
Bob Halley's avatar
Bob Halley committed
7
 * 
Bob Halley's avatar
base  
Bob Halley committed
8 9 10 11 12 13 14 15 16 17
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM 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
Bob Halley committed
18
#include <config.h>
Bob Halley's avatar
base  
Bob Halley committed
19 20 21

#include <stdio.h>
#include <stdlib.h>
Bob Halley's avatar
Bob Halley committed
22
#include <stddef.h>
Bob Halley's avatar
base  
Bob Halley committed
23 24
#include <string.h>

Michael Graff's avatar
Michael Graff committed
25 26
#include <limits.h>

Bob Halley's avatar
base  
Bob Halley committed
27
#include <isc/assertions.h>
28
#include <isc/error.h>
Bob Halley's avatar
Bob Halley committed
29
#include <isc/mem.h>
Bob Halley's avatar
Bob Halley committed
30

31
#ifndef ISC_SINGLETHREADED
Bob Halley's avatar
update  
Bob Halley committed
32
#include <isc/mutex.h>
Bob Halley's avatar
Bob Halley committed
33 34
#include "util.h"
#else
Bob Halley's avatar
Bob Halley committed
35 36
#define LOCK(l)
#define UNLOCK(l)
Bob Halley's avatar
update  
Bob Halley committed
37 38
#endif

Bob Halley's avatar
Bob Halley committed
39
#ifndef ISC_MEM_FILL
40 41
	/*
	 * XXXMPA
Bob Halley's avatar
Bob Halley committed
42
	 * We want this on during development to catch:
43 44 45 46 47 48
	 * 1. some reference after free bugs.
	 * 2. some failure to initalise bugs.
	 */
#define ISC_MEM_FILL 1
#endif

Bob Halley's avatar
base  
Bob Halley committed
49 50 51 52
/*
 * Types.
 */

Bob Halley's avatar
Bob Halley committed
53
typedef struct {
Bob Halley's avatar
base  
Bob Halley committed
54
	void *			next;
Bob Halley's avatar
Bob Halley committed
55
} element;
Bob Halley's avatar
base  
Bob Halley committed
56 57 58 59 60 61

typedef struct {
	size_t			size;
	/*
	 * This structure must be ALIGNMENT_SIZE bytes.
	 */
Bob Halley's avatar
Bob Halley committed
62
} size_info;
Bob Halley's avatar
base  
Bob Halley committed
63 64

struct stats {
Bob Halley's avatar
Bob Halley committed
65 66 67 68
	unsigned long		gets;
	unsigned long		totalgets;
	unsigned long		blocks;
	unsigned long		freefrags;
Bob Halley's avatar
base  
Bob Halley committed
69 70
};

71 72
#define MEM_MAGIC		0x4D656d43U	/* MemC. */
#define VALID_CONTEXT(c)	((c) != NULL && (c)->magic == MEM_MAGIC)
Bob Halley's avatar
Bob Halley committed
73

74
struct isc_mem {
Bob Halley's avatar
Bob Halley committed
75 76
	unsigned int		magic;
	isc_mutex_t		lock;
77 78 79
	isc_memalloc_t		memalloc;
	isc_memfree_t		memfree;
	void *			arg;
Bob Halley's avatar
base  
Bob Halley committed
80 81
	size_t			max_size;
	size_t			mem_target;
Bob Halley's avatar
Bob Halley committed
82 83
	element **		freelists;
	element *		basic_blocks;
84 85 86
	unsigned char **	basic_table;
	unsigned int		basic_table_count;
	unsigned int		basic_table_size;
Bob Halley's avatar
Bob Halley committed
87 88
	unsigned char *		lowest;
	unsigned char *		highest;
Bob Halley's avatar
base  
Bob Halley committed
89
	struct stats *		stats;
90
	size_t			quota;
Bob Halley's avatar
Bob Halley committed
91
	size_t			total;
Michael Graff's avatar
Michael Graff committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	ISC_LIST(isc_mempool_t)	pools;
};

#define MEMPOOL_MAGIC		0x4D454d70U	/* MEMp. */
#define VALID_MEMPOOL(c)	((c) != NULL && (c)->magic == MEMPOOL_MAGIC)

struct isc_mempool {
	unsigned int	magic;		/* magic number */
	ISC_LINK(isc_mempool_t)	link;	/* next pool in this mem context */
	isc_mem_t      *mctx;		/* our memory context */
	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 */
Bob Halley's avatar
base  
Bob Halley committed
111 112 113 114
};

/* Forward. */

Michael Graff's avatar
Michael Graff committed
115
static inline size_t		quantize(size_t);
Bob Halley's avatar
Bob Halley committed
116 117
static inline void		mem_putunlocked(isc_mem_t *, void *, size_t);
static inline void *		mem_getunlocked(isc_mem_t *, size_t);
Bob Halley's avatar
base  
Bob Halley committed
118

Bob Halley's avatar
Bob Halley committed
119
/* Constants. */
Bob Halley's avatar
base  
Bob Halley committed
120 121 122 123 124

#define DEF_MAX_SIZE		1100
#define DEF_MEM_TARGET		4096
#define ALIGNMENT_SIZE		sizeof (void *)
#define NUM_BASIC_BLOCKS	64			/* must be > 1 */
125
#define TABLE_INCREMENT		1024
Bob Halley's avatar
base  
Bob Halley committed
126 127 128

/* Private Inline-able. */

129
static inline size_t
Bob Halley's avatar
base  
Bob Halley committed
130
quantize(size_t size) {
131
	int temp;
Bob Halley's avatar
base  
Bob Halley committed
132 133

	/*
134
	 * Round up the result in order to get a size big
Bob Halley's avatar
base  
Bob Halley committed
135 136 137
	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
	 * byte boundaries.
	 */
138 139 140

	temp = size + (ALIGNMENT_SIZE - 1);
	return (temp - temp % ALIGNMENT_SIZE); 
Bob Halley's avatar
base  
Bob Halley committed
141 142
}

143 144 145 146 147 148 149 150 151 152 153
/* Private. */

static void *
default_memalloc(void *arg, size_t size) {
	(void)arg;
	return (malloc(size));
}

static void
default_memfree(void *arg, void *ptr) {
	(void)arg;
James Brister's avatar
James Brister committed
154
	free(ptr);
155 156
}

Bob Halley's avatar
base  
Bob Halley committed
157 158
/* Public. */

Bob Halley's avatar
Bob Halley committed
159
isc_result_t
160 161 162
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)
Bob Halley's avatar
Bob Halley committed
163
{
164
	isc_mem_t *ctx;
Bob Halley's avatar
Bob Halley committed
165 166

	REQUIRE(ctxp != NULL && *ctxp == NULL);
167 168
	REQUIRE(memalloc != NULL);
	REQUIRE(memfree != NULL);
Bob Halley's avatar
base  
Bob Halley committed
169

170
	ctx = (memalloc)(arg, sizeof *ctx);
Michael Graff's avatar
Michael Graff committed
171 172 173
	if (ctx == NULL)
		return (ISC_R_NOMEMORY);

Bob Halley's avatar
base  
Bob Halley committed
174 175 176 177 178 179 180 181
	if (init_max_size == 0)
		ctx->max_size = DEF_MAX_SIZE;
	else
		ctx->max_size = init_max_size;
	if (target_size == 0)
		ctx->mem_target = DEF_MEM_TARGET;
	else
		ctx->mem_target = target_size;
182 183 184 185
	ctx->memalloc = memalloc;
	ctx->memfree = memfree;
	ctx->arg = arg;
	ctx->freelists = (memalloc)(arg, ctx->max_size * sizeof (element *));
Bob Halley's avatar
base  
Bob Halley committed
186
	if (ctx->freelists == NULL) {
187
		(memfree)(arg, ctx);
Bob Halley's avatar
Bob Halley committed
188
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
189
	}
Bob Halley's avatar
Bob Halley committed
190 191
	memset(ctx->freelists, 0,
	       ctx->max_size * sizeof (element *));
192 193
	ctx->stats = (memalloc)(arg,
				(ctx->max_size+1) * sizeof (struct stats));
Bob Halley's avatar
base  
Bob Halley committed
194
	if (ctx->stats == NULL) {
195 196
		(memfree)(arg, ctx->freelists);
		(memfree)(arg, ctx);
Bob Halley's avatar
Bob Halley committed
197
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
198 199 200
	}
	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof (struct stats));
	ctx->basic_blocks = NULL;
201 202 203
	ctx->basic_table = NULL;
	ctx->basic_table_count = 0;
	ctx->basic_table_size = 0;
Bob Halley's avatar
base  
Bob Halley committed
204 205
	ctx->lowest = NULL;
	ctx->highest = NULL;
Bob Halley's avatar
Bob Halley committed
206
	if (isc_mutex_init(&ctx->lock) != ISC_R_SUCCESS) {
207 208 209
		(memfree)(arg, ctx->stats);
		(memfree)(arg, ctx->freelists);
		(memfree)(arg, ctx);
Bob Halley's avatar
Bob Halley committed
210
		UNEXPECTED_ERROR(__FILE__, __LINE__,
Bob Halley's avatar
Bob Halley committed
211
				 "isc_mutex_init() failed");
Bob Halley's avatar
Bob Halley committed
212
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update  
Bob Halley committed
213
	}
214
	ctx->quota = 0;
Bob Halley's avatar
Bob Halley committed
215
	ctx->total = 0;
Bob Halley's avatar
Bob Halley committed
216
	ctx->magic = MEM_MAGIC;
Michael Graff's avatar
Michael Graff committed
217 218
	ISC_LIST_INIT(ctx->pools);

Bob Halley's avatar
base  
Bob Halley committed
219
	*ctxp = ctx;
Bob Halley's avatar
Bob Halley committed
220
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
221 222
}

223 224 225 226 227 228 229 230 231
isc_result_t
isc_mem_create(size_t init_max_size, size_t target_size,
	       isc_mem_t **ctxp)
{
	return (isc_mem_createx(init_max_size, target_size,
				default_memalloc, default_memfree, NULL,
				ctxp));
}

Bob Halley's avatar
base  
Bob Halley committed
232
void
233
isc_mem_destroy(isc_mem_t **ctxp) {
234
	unsigned int i;
235
	isc_mem_t *ctx;
236

Bob Halley's avatar
base  
Bob Halley committed
237
	REQUIRE(ctxp != NULL);
238
	ctx = *ctxp;
Bob Halley's avatar
Bob Halley committed
239 240 241
	REQUIRE(VALID_CONTEXT(ctx));

	ctx->magic = 0;
242

Michael Graff's avatar
Michael Graff committed
243 244
	INSIST(ISC_LIST_EMPTY(ctx->pools));

245 246 247 248
	for (i = 0; i <= ctx->max_size; i++)
		INSIST(ctx->stats[i].gets == 0);

	for (i = 0; i < ctx->basic_table_count; i++)
249 250 251 252
		(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
	(ctx->memfree)(ctx->arg, ctx->freelists);
	(ctx->memfree)(ctx->arg, ctx->stats);
	(ctx->memfree)(ctx->arg, ctx->basic_table);
Bob Halley's avatar
Bob Halley committed
253
	(void)isc_mutex_destroy(&ctx->lock);
254
	(ctx->memfree)(ctx->arg, ctx);
Bob Halley's avatar
base  
Bob Halley committed
255 256 257 258

	*ctxp = NULL;
}

259 260 261 262 263 264 265 266 267 268 269
isc_result_t
isc_mem_restore(isc_mem_t *ctx) {
	isc_result_t result;

	result = isc_mutex_init(&ctx->lock); 
	if (result != ISC_R_SUCCESS)
		ctx->magic = 0;

	return (result);
}

270
static void
271
more_basic_blocks(isc_mem_t *ctx) {
272 273 274 275 276
	void *new;
	unsigned char *curr, *next;
	unsigned char *first, *last;
	unsigned char **table;
	unsigned int table_size;
Bob Halley's avatar
Bob Halley committed
277
	size_t increment;
278 279 280 281
	int i;

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

282 283 284
	/*
	 * Did we hit the quota for this context?
	 */
Bob Halley's avatar
Bob Halley committed
285 286 287
	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
	if (ctx->quota != 0 && ctx->total + increment > ctx->quota)
		return;
288 289 290

	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
	if (ctx->basic_table_count == ctx->basic_table_size) {
291
		table_size = ctx->basic_table_size + TABLE_INCREMENT;
292 293
		table = (ctx->memalloc)(ctx->arg,
					table_size * sizeof (unsigned char *));
294 295
		if (table == NULL)
			return;
296 297 298 299
		if (ctx->basic_table_size != 0) {
			memcpy(table, ctx->basic_table,
			       ctx->basic_table_size *
			       sizeof (unsigned char *));
300
			(ctx->memfree)(ctx->arg, ctx->basic_table);
301
		}
302 303
		ctx->basic_table = table;
		ctx->basic_table_size = table_size;
304
	}
305

306
	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
307
	if (new == NULL)
308
		return;
Bob Halley's avatar
Bob Halley committed
309
	ctx->total += increment;
310 311
	ctx->basic_table[ctx->basic_table_count] = new;
	ctx->basic_table_count++;
312

313 314 315
	curr = new;
	next = curr + ctx->mem_target;
	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
Bob Halley's avatar
Bob Halley committed
316
		((element *)curr)->next = next;
317 318 319 320 321 322 323
		curr = next;
		next += ctx->mem_target;
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
Bob Halley's avatar
Bob Halley committed
324
	((element *)curr)->next = NULL;
325 326 327 328 329 330 331 332 333
	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;
}

Bob Halley's avatar
base  
Bob Halley committed
334
void *
Bob Halley's avatar
Bob Halley committed
335
__isc_mem_get(isc_mem_t *ctx, size_t size)
Michael Graff's avatar
Michael Graff committed
336
{
Bob Halley's avatar
base  
Bob Halley committed
337 338 339
	void *ret;

	REQUIRE(size > 0);
Bob Halley's avatar
Bob Halley committed
340
	REQUIRE(VALID_CONTEXT(ctx));
Michael Graff's avatar
Michael Graff committed
341

Bob Halley's avatar
Bob Halley committed
342
	LOCK(&ctx->lock);
Bob Halley's avatar
Bob Halley committed
343
	ret = mem_getunlocked(ctx, size);
Michael Graff's avatar
Michael Graff committed
344 345 346 347 348 349
	UNLOCK(&ctx->lock);

	return (ret);
}

static inline void *
Bob Halley's avatar
Bob Halley committed
350
mem_getunlocked(isc_mem_t *ctx, size_t size)
Michael Graff's avatar
Michael Graff committed
351 352 353
{
	size_t new_size = quantize(size);
	void *ret;
Bob Halley's avatar
base  
Bob Halley committed
354 355 356

	if (size >= ctx->max_size || new_size >= ctx->max_size) {
		/* memget() was called on something beyond our upper limit. */
Bob Halley's avatar
Bob Halley committed
357 358 359 360
		if (ctx->quota != 0 && ctx->total + size > ctx->quota) {
			ret = NULL;
			goto done;
		}
361
		ret = (ctx->memalloc)(ctx->arg, size);
Bob Halley's avatar
base  
Bob Halley committed
362
		if (ret != NULL) {
Bob Halley's avatar
Bob Halley committed
363
			ctx->total += size;
Bob Halley's avatar
base  
Bob Halley committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
			ctx->stats[ctx->max_size].gets++;
			ctx->stats[ctx->max_size].totalgets++;
		}
		goto done;
	}

	/* 
	 * 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.
	 */
	if (ctx->freelists[new_size] == NULL) {
		int i, frags;
		size_t total_size;
		void *new;
Bob Halley's avatar
Bob Halley committed
379
		unsigned char *curr, *next;
Bob Halley's avatar
base  
Bob Halley committed
380 381

		if (ctx->basic_blocks == NULL) {
382 383
			more_basic_blocks(ctx);
			if (ctx->basic_blocks == NULL) {
Bob Halley's avatar
base  
Bob Halley committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397
				ret = NULL;
				goto done;
			}
		}
		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;
		for (i = 0; i < (frags - 1); i++) {
Bob Halley's avatar
Bob Halley committed
398
			((element *)curr)->next = next;
Bob Halley's avatar
base  
Bob Halley committed
399 400 401 402
			curr = next;
			next += new_size;
		}
		/* curr is now pointing at the last block in the array. */
Bob Halley's avatar
Bob Halley committed
403
		((element *)curr)->next = NULL;
Bob Halley's avatar
base  
Bob Halley committed
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
		ctx->freelists[new_size] = new;
	}

	/* The free list uses the "rounded-up" size "new_size": */
	ret = ctx->freelists[new_size];
	ctx->freelists[new_size] = ctx->freelists[new_size]->next;

	/* 
	 * 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--;

 done:

423 424 425 426 427
#if ISC_MEM_FILL
	if (ret != NULL)
		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
#endif

Bob Halley's avatar
base  
Bob Halley committed
428 429 430 431
	return (ret);
}

void
Bob Halley's avatar
Bob Halley committed
432
__isc_mem_put(isc_mem_t *ctx, void *mem, size_t size)
Michael Graff's avatar
Michael Graff committed
433
{
Bob Halley's avatar
base  
Bob Halley committed
434
	REQUIRE(size > 0);
Bob Halley's avatar
Bob Halley committed
435
	REQUIRE(VALID_CONTEXT(ctx));
Michael Graff's avatar
Michael Graff committed
436

Bob Halley's avatar
Bob Halley committed
437
	LOCK(&ctx->lock);
Bob Halley's avatar
Bob Halley committed
438
	mem_putunlocked(ctx, mem, size);
Michael Graff's avatar
Michael Graff committed
439 440 441 442
	UNLOCK(&ctx->lock);
}

static inline void
Bob Halley's avatar
Bob Halley committed
443
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size)
Michael Graff's avatar
Michael Graff committed
444 445
{
	size_t new_size = quantize(size);
Bob Halley's avatar
base  
Bob Halley committed
446

447
#if ISC_MEM_FILL
Bob Halley's avatar
Bob Halley committed
448
	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
449 450
#endif

Bob Halley's avatar
base  
Bob Halley committed
451 452
	if (size == ctx->max_size || new_size >= ctx->max_size) {
		/* memput() called on something beyond our upper limit */
453
		(ctx->memfree)(ctx->arg, mem);
Bob Halley's avatar
base  
Bob Halley committed
454 455
		INSIST(ctx->stats[ctx->max_size].gets != 0);
		ctx->stats[ctx->max_size].gets--;
Bob Halley's avatar
Bob Halley committed
456 457
		INSIST(size <= ctx->total);
		ctx->total -= size;
Michael Graff's avatar
Michael Graff committed
458
		return;
Bob Halley's avatar
base  
Bob Halley committed
459 460 461
	}

	/* The free list uses the "rounded-up" size "new_size": */
Bob Halley's avatar
Bob Halley committed
462 463
	((element *)mem)->next = ctx->freelists[new_size];
	ctx->freelists[new_size] = (element *)mem;
Bob Halley's avatar
base  
Bob Halley committed
464 465 466 467 468 469 470 471 472 473 474 475 476

	/* 
	 * 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.
	 */
	INSIST(ctx->stats[size].gets != 0);
	ctx->stats[size].gets--;
	ctx->stats[new_size].freefrags++;
}

void *
477
__isc_mem_getdebug(isc_mem_t *ctx, size_t size, const char *file, int line) {
Bob Halley's avatar
base  
Bob Halley committed
478
	void *ptr;
Bob Halley's avatar
Bob Halley committed
479

Bob Halley's avatar
Bob Halley committed
480
	ptr = __isc_mem_get(ctx, size);
Bob Halley's avatar
base  
Bob Halley committed
481
	fprintf(stderr, "%s:%d: mem_get(%p, %lu) -> %p\n", file, line,
Bob Halley's avatar
Bob Halley committed
482
		ctx, (unsigned long)size, ptr);
Bob Halley's avatar
base  
Bob Halley committed
483 484 485 486
	return (ptr);
}

void
487
__isc_mem_putdebug(isc_mem_t *ctx, void *ptr, size_t size, const char *file,
Bob Halley's avatar
Bob Halley committed
488
		 int line)
Bob Halley's avatar
base  
Bob Halley committed
489 490
{
	fprintf(stderr, "%s:%d: mem_put(%p, %p, %lu)\n", file, line, 
Bob Halley's avatar
Bob Halley committed
491
		ctx, ptr, (unsigned long)size);
Bob Halley's avatar
Bob Halley committed
492
	__isc_mem_put(ctx, ptr, size);
Bob Halley's avatar
base  
Bob Halley committed
493 494 495 496 497 498
}

/*
 * Print the stats[] on the stream "out" with suitable formatting.
 */
void
Michael Graff's avatar
Michael Graff committed
499 500
isc_mem_stats(isc_mem_t *ctx, FILE *out)
{
Bob Halley's avatar
base  
Bob Halley committed
501
	size_t i;
Michael Graff's avatar
Michael Graff committed
502 503
	const struct stats *s;
	const isc_mempool_t *pool;
Bob Halley's avatar
base  
Bob Halley committed
504

Bob Halley's avatar
Bob Halley committed
505 506
	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
507

Michael Graff's avatar
Michael Graff committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	if (ctx->freelists != NULL) {
		for (i = 1; i <= ctx->max_size; i++) {
			s = &ctx->stats[i];

			if (s->totalgets == 0 && s->gets == 0)
				continue;
			fprintf(out, "%s%5d: %11lu gets, %11lu rem",
				(i == ctx->max_size) ? ">=" : "  ",
				i, s->totalgets, s->gets);
			if (s->blocks != 0)
				fprintf(out, " (%lu bl, %lu ff)",
					s->blocks, s->freefrags);
			fputc('\n', out);
		}
	}

	pool = ISC_LIST_HEAD(ctx->pools);
	if (pool != NULL) {
		fprintf(out, "[Pool statistics]\n");
		fprintf(out, "%10s %10s %10s %10s %10s %10s %10s\n",
			"size", "maxalloc", "allocated", "freecount",
			"freemax", "fillcount", "gets");
	}
	while (pool != NULL) {
		fprintf(out, "%10u %10u %10u %10u %10u %10u %10u\n",
			pool->size, pool->maxalloc, pool->allocated,
			pool->freecount, pool->freemax, pool->fillcount,
			pool->gets);
		pool = ISC_LIST_NEXT(pool, link);
Bob Halley's avatar
base  
Bob Halley committed
537 538
	}

Bob Halley's avatar
Bob Halley committed
539
	UNLOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
540 541
}

Bob Halley's avatar
Bob Halley committed
542
isc_boolean_t
543
isc_mem_valid(isc_mem_t *ctx, void *ptr) {
Bob Halley's avatar
Bob Halley committed
544
	unsigned char *cp = ptr;
Bob Halley's avatar
Bob Halley committed
545
	isc_boolean_t result = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
546

Bob Halley's avatar
Bob Halley committed
547 548
	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
549 550

	if (ctx->lowest != NULL && cp >= ctx->lowest && cp <= ctx->highest)
Bob Halley's avatar
Bob Halley committed
551
		result = ISC_TRUE;
Bob Halley's avatar
base  
Bob Halley committed
552

Bob Halley's avatar
Bob Halley committed
553
	UNLOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
554

Bob Halley's avatar
Bob Halley committed
555
	return (result);
Bob Halley's avatar
base  
Bob Halley committed
556 557 558 559 560 561 562
}

/*
 * Replacements for malloc() and free().
 */

void *
563
isc_mem_allocate(isc_mem_t *ctx, size_t size) {
Bob Halley's avatar
Bob Halley committed
564
	size_info *si;
Bob Halley's avatar
base  
Bob Halley committed
565 566

	size += ALIGNMENT_SIZE;
Bob Halley's avatar
Bob Halley committed
567
	si = isc_mem_get(ctx, size);
Bob Halley's avatar
base  
Bob Halley committed
568 569 570 571 572 573 574
	if (si == NULL)
		return (NULL);
	si->size = size;
	return (&si[1]);
}

void
575
isc_mem_free(isc_mem_t *ctx, void *ptr) {
Bob Halley's avatar
Bob Halley committed
576
	size_info *si;
Bob Halley's avatar
base  
Bob Halley committed
577

Bob Halley's avatar
Bob Halley committed
578
	si = &(((size_info *)ptr)[-1]);
Bob Halley's avatar
Bob Halley committed
579
	isc_mem_put(ctx, si, si->size);
Bob Halley's avatar
base  
Bob Halley committed
580 581
}

Bob Halley's avatar
Bob Halley committed
582 583 584 585
/*
 * Other useful things.
 */

Bob Halley's avatar
Bob Halley committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599
char *
isc_mem_strdup(isc_mem_t *mctx, const char *s) {
	size_t len;
	char *ns;

	len = strlen(s);
	ns = isc_mem_allocate(mctx, len + 1);
	if (ns == NULL)
		return (NULL);
	strncpy(ns, s, len + 1);
	
	return (ns);
}

600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
/*
 * Quotas
 */

void
isc_mem_setquota(isc_mem_t *ctx, size_t quota) {
	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);

	ctx->quota = quota;

	UNLOCK(&ctx->lock);
}

size_t
isc_mem_getquota(isc_mem_t *ctx) {
	size_t quota;

	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);

	quota = ctx->quota;

	UNLOCK(&ctx->lock);

	return (quota);
}

Bob Halley's avatar
Bob Halley committed
628 629
#ifdef ISC_MEMCLUSTER_LEGACY

Bob Halley's avatar
base  
Bob Halley committed
630 631 632 633
/*
 * Public Legacy.
 */

634
static isc_mem_t *default_context = NULL;
Bob Halley's avatar
Bob Halley committed
635

Bob Halley's avatar
base  
Bob Halley committed
636 637 638 639 640
int
meminit(size_t init_max_size, size_t target_size) {
	/* need default_context lock here */
	if (default_context != NULL)
		return (-1);
Bob Halley's avatar
Bob Halley committed
641
	return (isc_mem_create(init_max_size, target_size, &default_context));
Bob Halley's avatar
base  
Bob Halley committed
642 643
}

644
isc_mem_t *
Bob Halley's avatar
base  
Bob Halley committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
mem_default_context(void) {
	/* need default_context lock here */
	if (default_context == NULL && meminit(0, 0) == -1)
		return (NULL);
	return (default_context);
}

void *
__memget(size_t size) {
	/* need default_context lock here */
	if (default_context == NULL && meminit(0, 0) == -1)
		return (NULL);
	return (__mem_get(default_context, size));
}

void
__memput(void *mem, size_t size) {
	/* need default_context lock here */
	REQUIRE(default_context != NULL);
	__mem_put(default_context, mem, size);
}

void *
__memget_debug(size_t size, const char *file, int line) {
	void *ptr;
	ptr = __memget(size);
	fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line,
Bob Halley's avatar
Bob Halley committed
672
		(unsigned long)size, ptr);
Bob Halley's avatar
base  
Bob Halley committed
673 674 675 676 677 678
	return (ptr);
}

void
__memput_debug(void *ptr, size_t size, const char *file, int line) {
	fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, 
Bob Halley's avatar
Bob Halley committed
679
		ptr, (unsigned long)size);
Bob Halley's avatar
base  
Bob Halley committed
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
	__memput(ptr, size);
}

int
memvalid(void *ptr) {
	/* need default_context lock here */
	REQUIRE(default_context != NULL);
	return (mem_valid(default_context, ptr));
}

void
memstats(FILE *out) {
	/* need default_context lock here */
	REQUIRE(default_context != NULL);
	mem_stats(default_context, out);
}
Bob Halley's avatar
Bob Halley committed
696 697

#endif /* ISC_MEMCLUSTER_LEGACY */
Michael Graff's avatar
Michael Graff committed
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725


/*
 * Memory pool stuff
 */


#if 0
/*
 * Free all but "n" items from the pool's free list.  If n == 0, all items
 * will be returned to the mctx.
 */
static void
mempool_release(isc_mempool_t *mpctx, unsigned int n)
{
	isc_mem_t *mctx;
	element *item;
	element *next;
	unsigned int count;

	mctx = mpctx->mctx;

	if (mpctx->freecount <= n)
		return;

	INSIST(mpctx->items != NULL);
	item = mpctx->items;
	for (count = 0 ; count < n ; count++) {
Michael Graff's avatar
Michael Graff committed
726
		item = item->next;
Michael Graff's avatar
Michael Graff committed
727 728 729 730 731 732 733 734 735
		INSIST(item != NULL);
	}

	/*
	 * All remaining items are to be freed.  Lock the context once,
	 * free them all, and unlock the context.
	 */
	LOCK(&mctx->lock);
	do {
Michael Graff's avatar
Michael Graff committed
736
		next = item->next;
Michael Graff's avatar
Michael Graff committed
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
		mem_putunlocked(mctx, item, mpctx->size);
		INSIST(mpctx->freecount > 0);
		mpctx->freecount--;
		item = next;
	} while (item != NULL);
	UNLOCK(&mctx->lock);
}
#endif

/*
 * Release all items on the free list.  No locking is done, the memory
 * context must be locked.
 */
static void
mempool_releaseall(isc_mempool_t *mpctx)
{
	isc_mem_t *mctx;
	element *item;
	element *next;

	mctx = mpctx->mctx;

	if (mpctx->freecount == 0)
		return;

	INSIST(mpctx->items != NULL);
	item = mpctx->items;

	do {
Michael Graff's avatar
Michael Graff committed
766
		next = item->next;
Bob Halley's avatar
Bob Halley committed
767
		mem_putunlocked(mctx, item, mpctx->size);
Michael Graff's avatar
Michael Graff committed
768 769 770 771 772 773 774
		INSIST(mpctx->freecount > 0);
		mpctx->freecount--;
		item = next;
	} while (item != NULL);
}

isc_result_t
Michael Graff's avatar
Michael Graff committed
775
isc_mempool_create(isc_mem_t *mctx, size_t size,
Michael Graff's avatar
Michael Graff committed
776 777 778 779 780
		   isc_mempool_t **mpctxp)
{
	isc_mempool_t *mpctx;

	REQUIRE(VALID_CONTEXT(mctx));
Michael Graff's avatar
Michael Graff committed
781
	REQUIRE(size > 0);
Michael Graff's avatar
Michael Graff committed
782 783 784 785 786 787 788 789
	REQUIRE(mpctxp != NULL && *mpctxp == NULL);

	/*
	 * Allocate space for this pool, initialize values, and if all works
	 * well, attach to the memory context.
	 */
	LOCK(&mctx->lock);

Bob Halley's avatar
Bob Halley committed
790
	mpctx = mem_getunlocked(mctx, sizeof(isc_mempool_t));
Michael Graff's avatar
Michael Graff committed
791 792 793 794 795 796 797
	if (mpctx == NULL) {
		UNLOCK(&mctx->lock);
		return (ISC_R_NOMEMORY);
	}

	mpctx->magic = MEMPOOL_MAGIC;
	mpctx->mctx = mctx;
Michael Graff's avatar
Michael Graff committed
798
	mpctx->size = size;
Michael Graff's avatar
Michael Graff committed
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
	mpctx->maxalloc = UINT_MAX;
	mpctx->allocated = 0;
	mpctx->freecount = 0;
	mpctx->freemax = 1;
	mpctx->fillcount = 1;
	mpctx->gets = 0;
	mpctx->items = NULL;

	*mpctxp = mpctx;

	ISC_LIST_APPEND(mctx->pools, mpctx, link);

	UNLOCK(&mctx->lock);

	return (ISC_R_SUCCESS);
}

void
isc_mempool_destroy(isc_mempool_t **mpctxp)
{
	isc_mempool_t *mpctx;
	isc_mem_t *mctx;

	REQUIRE(mpctxp != NULL);
	mpctx = *mpctxp;
	REQUIRE(VALID_MEMPOOL(mpctx));
	REQUIRE(mpctx->allocated == 0);

	mctx = mpctx->mctx;

	LOCK(&mctx->lock);

	/*
	 * Return any items on the free list
	 */
	mempool_releaseall(mpctx);

	/*
	 * Remove our linked list entry from the memory context.
	 */
	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
	
	mpctx->magic = 0;

Bob Halley's avatar
Bob Halley committed
843
	mem_putunlocked(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
Michael Graff's avatar
Michael Graff committed
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885

	UNLOCK(&mctx->lock);

	*mpctxp = NULL;
}

void *
__isc_mempool_get(isc_mempool_t *mpctx)
{
	element *item;
	isc_mem_t *mctx;
	unsigned int i;

	REQUIRE(VALID_MEMPOOL(mpctx));

	mctx = mpctx->mctx;

	/*
	 * Don't let the caller go over quota
	 */
	if (mpctx->allocated >= mpctx->maxalloc)
		return (NULL);

	/*
	 * if we have a free list item, return the first here
	 */
	item = mpctx->items;
	if (item != NULL) {
		mpctx->items = item->next;
		INSIST(mpctx->freecount > 0);
		mpctx->freecount--;
		mpctx->gets++;
		mpctx->allocated++;
		return (item);
	}

	/*
	 * We need to dip into the well.  Lock the memory context here and
	 * fill up our free list.
	 */
	LOCK(&mctx->lock);
	for (i = 0 ; i < mpctx->fillcount ; i++) {
Bob Halley's avatar
Bob Halley committed
886
		item = mem_getunlocked(mctx, mpctx->size);
Michael Graff's avatar
Michael Graff committed
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
		if (item == NULL)
			break;
		item->next = mpctx->items;
		mpctx->items = item;
		mpctx->freecount++;
	}
	UNLOCK(&mctx->lock);

	/*
	 * If we didn't get any items, return NULL.
	 */
	item = mpctx->items;
	if (item == NULL)
		return (NULL);

	mpctx->items = item->next;
	mpctx->freecount--;
	mpctx->gets++;
	mpctx->allocated++;
	return (item);
}

void
__isc_mempool_put(isc_mempool_t *mpctx, void *mem)
{
	isc_mem_t *mctx;
	element *item;

	REQUIRE(VALID_MEMPOOL(mpctx));
	REQUIRE(mem != NULL);

	mctx = mpctx->mctx;

	INSIST(mpctx->allocated > 0);
	mpctx->allocated--;

	/*
	 * If our free list is full, return this to the mctx directly.
	 */
	if (mpctx->freecount >= mpctx->freemax) {
Bob Halley's avatar
Bob Halley committed
927
		__isc_mem_put(mctx, mem, mpctx->size);
Michael Graff's avatar
Michael Graff committed
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
		return;
	}

	/*
	 * Otherwise, attach it to our free list and bump the counter.
	 */
	mpctx->freecount++;
	item = (element *)mem;
	item->next = mpctx->items;
	mpctx->items = item;
}

void *
__isc_mempool_getdebug(isc_mempool_t *mpctx,
		       const char *file, int line)
{
	void *ptr;

	ptr = __isc_mempool_get(mpctx);
	fprintf(stderr, "%s:%d: mempool_get(%p) -> %p\n", file, line,
		mpctx, ptr);

	return (ptr);
}

void
__isc_mempool_putdebug(isc_mempool_t *mpctx, void *ptr,
		       const char *file, int line)
{
	fprintf(stderr, "%s:%d: mempool_put(%p, %p)\n", file, line, 
		mpctx, ptr);
	__isc_mempool_put(mpctx, ptr);
}

/*
 * Quotas
 */

void
isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit)
{
	REQUIRE(VALID_MEMPOOL(mpctx));

	mpctx->freemax = limit;
}

unsigned int
isc_mempool_getfreemax(isc_mempool_t *mpctx)
{
	REQUIRE(VALID_MEMPOOL(mpctx));

	return (mpctx->freemax);
}

unsigned int
isc_mempool_getfreecount(isc_mempool_t *mpctx)
{
	REQUIRE(VALID_MEMPOOL(mpctx));

	return (mpctx->freecount);
}

void
isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit)
{
	REQUIRE(limit > 0);

	REQUIRE(VALID_MEMPOOL(mpctx));

	mpctx->maxalloc = limit;
}

unsigned int
isc_mempool_getmaxalloc(isc_mempool_t *mpctx)
{
	REQUIRE(VALID_MEMPOOL(mpctx));

	return (mpctx->maxalloc);
}

unsigned int
isc_mempool_getallocated(isc_mempool_t *mpctx)
{
	REQUIRE(VALID_MEMPOOL(mpctx));

	return (mpctx->allocated);
}

void
isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit)
{
	REQUIRE(limit > 0);
	REQUIRE(VALID_MEMPOOL(mpctx));

	mpctx->fillcount = limit;
}

unsigned int
isc_mempool_getfillcount(isc_mempool_t *mpctx)
{
	REQUIRE(VALID_MEMPOOL(mpctx));

	return (mpctx->fillcount);
}