mem.c 20.1 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
base  
Bob Halley committed
39
40
41
42
43
44
/*
 * Types.
 */

typedef struct {
	void *			next;
Bob Halley's avatar
Bob Halley committed
45
} element;
Bob Halley's avatar
base  
Bob Halley committed
46
47
48
49
50
51

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

struct stats {
Bob Halley's avatar
Bob Halley committed
55
56
57
58
	unsigned long		gets;
	unsigned long		totalgets;
	unsigned long		blocks;
	unsigned long		freefrags;
Bob Halley's avatar
base  
Bob Halley committed
59
60
};

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

64
struct isc_mem {
Bob Halley's avatar
Bob Halley committed
65
66
	unsigned int		magic;
	isc_mutex_t		lock;
Bob Halley's avatar
base  
Bob Halley committed
67
68
	size_t			max_size;
	size_t			mem_target;
Bob Halley's avatar
Bob Halley committed
69
70
	element **		freelists;
	element *		basic_blocks;
71
72
73
	unsigned char **	basic_table;
	unsigned int		basic_table_count;
	unsigned int		basic_table_size;
Bob Halley's avatar
Bob Halley committed
74
75
	unsigned char *		lowest;
	unsigned char *		highest;
Bob Halley's avatar
base  
Bob Halley committed
76
	struct stats *		stats;
77
	size_t			quota;
Bob Halley's avatar
Bob Halley committed
78
	size_t			total;
Michael Graff's avatar
Michael Graff committed
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
	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
98
99
100
101
};

/* Forward. */

Michael Graff's avatar
Michael Graff committed
102
103
104
static inline size_t		quantize(size_t);
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
105

Bob Halley's avatar
Bob Halley committed
106
/* Constants. */
Bob Halley's avatar
base  
Bob Halley committed
107
108
109
110
111

#define DEF_MAX_SIZE		1100
#define DEF_MEM_TARGET		4096
#define ALIGNMENT_SIZE		sizeof (void *)
#define NUM_BASIC_BLOCKS	64			/* must be > 1 */
112
#define TABLE_INCREMENT		1024
Bob Halley's avatar
base  
Bob Halley committed
113
114
115

/* Private Inline-able. */

116
static inline size_t
Bob Halley's avatar
base  
Bob Halley committed
117
quantize(size_t size) {
118
	int temp;
Bob Halley's avatar
base  
Bob Halley committed
119
120

	/*
121
	 * Round up the result in order to get a size big
Bob Halley's avatar
base  
Bob Halley committed
122
123
124
	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
	 * byte boundaries.
	 */
125
126
127

	temp = size + (ALIGNMENT_SIZE - 1);
	return (temp - temp % ALIGNMENT_SIZE); 
Bob Halley's avatar
base  
Bob Halley committed
128
129
130
131
}

/* Public. */

Bob Halley's avatar
Bob Halley committed
132
isc_result_t
133
134
isc_mem_create(size_t init_max_size, size_t target_size,
	       isc_mem_t **ctxp)
Bob Halley's avatar
Bob Halley committed
135
{
136
	isc_mem_t *ctx;
Bob Halley's avatar
Bob Halley committed
137
138

	REQUIRE(ctxp != NULL && *ctxp == NULL);
Bob Halley's avatar
base  
Bob Halley committed
139
140

	ctx = malloc(sizeof *ctx);
Michael Graff's avatar
Michael Graff committed
141
142
143
	if (ctx == NULL)
		return (ISC_R_NOMEMORY);

Bob Halley's avatar
base  
Bob Halley committed
144
145
146
147
148
149
150
151
	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;
Bob Halley's avatar
Bob Halley committed
152
	ctx->freelists = malloc(ctx->max_size * sizeof (element *));
Bob Halley's avatar
base  
Bob Halley committed
153
154
	if (ctx->freelists == NULL) {
		free(ctx);
Bob Halley's avatar
Bob Halley committed
155
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
156
157
	}
	memset(ctx->freelists, 0,
Bob Halley's avatar
Bob Halley committed
158
	       ctx->max_size * sizeof (element *));
Bob Halley's avatar
base  
Bob Halley committed
159
160
161
162
	ctx->stats = malloc((ctx->max_size+1) * sizeof (struct stats));
	if (ctx->stats == NULL) {
		free(ctx->freelists);
		free(ctx);
Bob Halley's avatar
Bob Halley committed
163
		return (ISC_R_NOMEMORY);
Bob Halley's avatar
base  
Bob Halley committed
164
165
166
	}
	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof (struct stats));
	ctx->basic_blocks = NULL;
167
168
169
	ctx->basic_table = NULL;
	ctx->basic_table_count = 0;
	ctx->basic_table_size = 0;
Bob Halley's avatar
base  
Bob Halley committed
170
171
	ctx->lowest = NULL;
	ctx->highest = NULL;
Bob Halley's avatar
Bob Halley committed
172
	if (isc_mutex_init(&ctx->lock) != ISC_R_SUCCESS) {
Bob Halley's avatar
update    
Bob Halley committed
173
174
175
		free(ctx->stats);
		free(ctx->freelists);
		free(ctx);
Bob Halley's avatar
Bob Halley committed
176
		UNEXPECTED_ERROR(__FILE__, __LINE__,
Bob Halley's avatar
Bob Halley committed
177
				 "isc_mutex_init() failed");
Bob Halley's avatar
Bob Halley committed
178
		return (ISC_R_UNEXPECTED);
Bob Halley's avatar
update    
Bob Halley committed
179
	}
180
	ctx->quota = 0;
Bob Halley's avatar
Bob Halley committed
181
	ctx->total = 0;
Bob Halley's avatar
Bob Halley committed
182
	ctx->magic = MEM_MAGIC;
Michael Graff's avatar
Michael Graff committed
183
184
	ISC_LIST_INIT(ctx->pools);

Bob Halley's avatar
base  
Bob Halley committed
185
	*ctxp = ctx;
Bob Halley's avatar
Bob Halley committed
186
	return (ISC_R_SUCCESS);
Bob Halley's avatar
base  
Bob Halley committed
187
188
189
}

void
190
isc_mem_destroy(isc_mem_t **ctxp) {
191
	unsigned int i;
192
	isc_mem_t *ctx;
193

Bob Halley's avatar
base  
Bob Halley committed
194
	REQUIRE(ctxp != NULL);
195
	ctx = *ctxp;
Bob Halley's avatar
Bob Halley committed
196
197
198
	REQUIRE(VALID_CONTEXT(ctx));

	ctx->magic = 0;
199

Michael Graff's avatar
Michael Graff committed
200
201
	INSIST(ISC_LIST_EMPTY(ctx->pools));

202
203
204
205
206
207
208
209
	for (i = 0; i <= ctx->max_size; i++)
		INSIST(ctx->stats[i].gets == 0);

	for (i = 0; i < ctx->basic_table_count; i++)
		free(ctx->basic_table[i]);
	free(ctx->freelists);
	free(ctx->stats);
	free(ctx->basic_table);
Bob Halley's avatar
Bob Halley committed
210
	(void)isc_mutex_destroy(&ctx->lock);
211
	free(ctx);
Bob Halley's avatar
base  
Bob Halley committed
212
213
214
215

	*ctxp = NULL;
}

216
static void
217
more_basic_blocks(isc_mem_t *ctx) {
218
219
220
221
222
	void *new;
	unsigned char *curr, *next;
	unsigned char *first, *last;
	unsigned char **table;
	unsigned int table_size;
Bob Halley's avatar
Bob Halley committed
223
	size_t increment;
224
225
226
227
	int i;

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

228
229
230
	/*
	 * Did we hit the quota for this context?
	 */
Bob Halley's avatar
Bob Halley committed
231
232
233
	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
	if (ctx->quota != 0 && ctx->total + increment > ctx->quota)
		return;
234
235
236

	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
	if (ctx->basic_table_count == ctx->basic_table_size) {
237
238
239
240
		table_size = ctx->basic_table_size + TABLE_INCREMENT;
		table = malloc(table_size * sizeof (unsigned char *));
		if (table == NULL)
			return;
241
242
243
244
245
246
		if (ctx->basic_table_size != 0) {
			memcpy(table, ctx->basic_table,
			       ctx->basic_table_size *
			       sizeof (unsigned char *));
			free(ctx->basic_table);
		}
247
248
		ctx->basic_table = table;
		ctx->basic_table_size = table_size;
249
	}
250
251

	new = malloc(NUM_BASIC_BLOCKS * ctx->mem_target);
252
	if (new == NULL)
253
		return;
Bob Halley's avatar
Bob Halley committed
254
	ctx->total += increment;
255
256
	ctx->basic_table[ctx->basic_table_count] = new;
	ctx->basic_table_count++;
257

258
259
260
	curr = new;
	next = curr + ctx->mem_target;
	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
Bob Halley's avatar
Bob Halley committed
261
		((element *)curr)->next = next;
262
263
264
265
266
267
268
		curr = next;
		next += ctx->mem_target;
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
Bob Halley's avatar
Bob Halley committed
269
	((element *)curr)->next = NULL;
270
271
272
273
274
275
276
277
278
	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
279
void *
Michael Graff's avatar
Michael Graff committed
280
281
__isc_mem_get(isc_mem_t *ctx, size_t size)
{
Bob Halley's avatar
base  
Bob Halley committed
282
283
284
	void *ret;

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

Bob Halley's avatar
Bob Halley committed
287
	LOCK(&ctx->lock);
Michael Graff's avatar
Michael Graff committed
288
289
290
291
292
293
294
295
296
297
298
	ret = mem_getunlocked(ctx, size);
	UNLOCK(&ctx->lock);

	return (ret);
}

static inline void *
mem_getunlocked(isc_mem_t *ctx, size_t size)
{
	size_t new_size = quantize(size);
	void *ret;
Bob Halley's avatar
base  
Bob Halley committed
299
300
301

	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
302
303
304
305
		if (ctx->quota != 0 && ctx->total + size > ctx->quota) {
			ret = NULL;
			goto done;
		}
Bob Halley's avatar
base  
Bob Halley committed
306
307
		ret = malloc(size);
		if (ret != NULL) {
Bob Halley's avatar
Bob Halley committed
308
			ctx->total += size;
Bob Halley's avatar
base  
Bob Halley committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
			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
324
		unsigned char *curr, *next;
Bob Halley's avatar
base  
Bob Halley committed
325
326

		if (ctx->basic_blocks == NULL) {
327
328
			more_basic_blocks(ctx);
			if (ctx->basic_blocks == NULL) {
Bob Halley's avatar
base  
Bob Halley committed
329
330
331
332
333
334
335
336
337
338
339
340
341
342
				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
343
			((element *)curr)->next = next;
Bob Halley's avatar
base  
Bob Halley committed
344
345
346
347
			curr = next;
			next += new_size;
		}
		/* curr is now pointing at the last block in the array. */
Bob Halley's avatar
Bob Halley committed
348
		((element *)curr)->next = NULL;
Bob Halley's avatar
base  
Bob Halley committed
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
		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:

368
369
370
371
372
#if ISC_MEM_FILL
	if (ret != NULL)
		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
#endif

Bob Halley's avatar
base  
Bob Halley committed
373
374
375
376
	return (ret);
}

void
Michael Graff's avatar
Michael Graff committed
377
378
__isc_mem_put(isc_mem_t *ctx, void *mem, size_t size)
{
Bob Halley's avatar
base  
Bob Halley committed
379
	REQUIRE(size > 0);
Bob Halley's avatar
Bob Halley committed
380
	REQUIRE(VALID_CONTEXT(ctx));
Michael Graff's avatar
Michael Graff committed
381

Bob Halley's avatar
Bob Halley committed
382
	LOCK(&ctx->lock);
Michael Graff's avatar
Michael Graff committed
383
384
385
386
387
388
389
390
	mem_putunlocked(ctx, mem, size);
	UNLOCK(&ctx->lock);
}

static inline void
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size)
{
	size_t new_size = quantize(size);
Bob Halley's avatar
base  
Bob Halley committed
391

392
393
394
395
#if ISC_MEM_FILL
	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
#endif

Bob Halley's avatar
base  
Bob Halley committed
396
397
398
399
400
	if (size == ctx->max_size || new_size >= ctx->max_size) {
		/* memput() called on something beyond our upper limit */
		free(mem);
		INSIST(ctx->stats[ctx->max_size].gets != 0);
		ctx->stats[ctx->max_size].gets--;
Bob Halley's avatar
Bob Halley committed
401
402
		INSIST(size <= ctx->total);
		ctx->total -= size;
Michael Graff's avatar
Michael Graff committed
403
		return;
Bob Halley's avatar
base  
Bob Halley committed
404
405
406
	}

	/* The free list uses the "rounded-up" size "new_size": */
Bob Halley's avatar
Bob Halley committed
407
408
	((element *)mem)->next = ctx->freelists[new_size];
	ctx->freelists[new_size] = (element *)mem;
Bob Halley's avatar
base  
Bob Halley committed
409
410
411
412
413
414
415
416
417
418
419
420
421

	/* 
	 * 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 *
422
__isc_mem_getdebug(isc_mem_t *ctx, size_t size, const char *file, int line) {
Bob Halley's avatar
base  
Bob Halley committed
423
	void *ptr;
Bob Halley's avatar
Bob Halley committed
424

425
	ptr = __isc_mem_get(ctx, size);
Bob Halley's avatar
base  
Bob Halley committed
426
	fprintf(stderr, "%s:%d: mem_get(%p, %lu) -> %p\n", file, line,
Bob Halley's avatar
Bob Halley committed
427
		ctx, (unsigned long)size, ptr);
Bob Halley's avatar
base  
Bob Halley committed
428
429
430
431
	return (ptr);
}

void
432
__isc_mem_putdebug(isc_mem_t *ctx, void *ptr, size_t size, const char *file,
Bob Halley's avatar
Bob Halley committed
433
		 int line)
Bob Halley's avatar
base  
Bob Halley committed
434
435
{
	fprintf(stderr, "%s:%d: mem_put(%p, %p, %lu)\n", file, line, 
Bob Halley's avatar
Bob Halley committed
436
		ctx, ptr, (unsigned long)size);
437
	__isc_mem_put(ctx, ptr, size);
Bob Halley's avatar
base  
Bob Halley committed
438
439
440
441
442
443
}

/*
 * Print the stats[] on the stream "out" with suitable formatting.
 */
void
Michael Graff's avatar
Michael Graff committed
444
445
isc_mem_stats(isc_mem_t *ctx, FILE *out)
{
Bob Halley's avatar
base  
Bob Halley committed
446
	size_t i;
Michael Graff's avatar
Michael Graff committed
447
448
	const struct stats *s;
	const isc_mempool_t *pool;
Bob Halley's avatar
base  
Bob Halley committed
449

Bob Halley's avatar
Bob Halley committed
450
451
	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
452

Michael Graff's avatar
Michael Graff committed
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
	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
482
483
	}

Bob Halley's avatar
Bob Halley committed
484
	UNLOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
485
486
}

Bob Halley's avatar
Bob Halley committed
487
isc_boolean_t
488
isc_mem_valid(isc_mem_t *ctx, void *ptr) {
Bob Halley's avatar
Bob Halley committed
489
	unsigned char *cp = ptr;
Bob Halley's avatar
Bob Halley committed
490
	isc_boolean_t result = ISC_FALSE;
Bob Halley's avatar
base  
Bob Halley committed
491

Bob Halley's avatar
Bob Halley committed
492
493
	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
494
495

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

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

Bob Halley's avatar
Bob Halley committed
500
	return (result);
Bob Halley's avatar
base  
Bob Halley committed
501
502
503
504
505
506
507
}

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

void *
508
isc_mem_allocate(isc_mem_t *ctx, size_t size) {
Bob Halley's avatar
Bob Halley committed
509
	size_info *si;
Bob Halley's avatar
base  
Bob Halley committed
510
511

	size += ALIGNMENT_SIZE;
Bob Halley's avatar
Bob Halley committed
512
	si = isc_mem_get(ctx, size);
Bob Halley's avatar
base  
Bob Halley committed
513
514
515
516
517
518
519
	if (si == NULL)
		return (NULL);
	si->size = size;
	return (&si[1]);
}

void
520
isc_mem_free(isc_mem_t *ctx, void *ptr) {
Bob Halley's avatar
Bob Halley committed
521
	size_info *si;
Bob Halley's avatar
base  
Bob Halley committed
522

Bob Halley's avatar
Bob Halley committed
523
	si = &(((size_info *)ptr)[-1]);
Bob Halley's avatar
Bob Halley committed
524
	isc_mem_put(ctx, si, si->size);
Bob Halley's avatar
base  
Bob Halley committed
525
526
}

Bob Halley's avatar
Bob Halley committed
527
528
529
530
/*
 * Other useful things.
 */

Bob Halley's avatar
Bob Halley committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
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);
}

545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/*
 * 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
573
574
#ifdef ISC_MEMCLUSTER_LEGACY

Bob Halley's avatar
base  
Bob Halley committed
575
576
577
578
/*
 * Public Legacy.
 */

579
static isc_mem_t *default_context = NULL;
Bob Halley's avatar
Bob Halley committed
580

Bob Halley's avatar
base  
Bob Halley committed
581
582
583
584
585
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
586
	return (isc_mem_create(init_max_size, target_size, &default_context));
Bob Halley's avatar
base  
Bob Halley committed
587
588
}

589
isc_mem_t *
Bob Halley's avatar
base  
Bob Halley committed
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
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
617
		(unsigned long)size, ptr);
Bob Halley's avatar
base  
Bob Halley committed
618
619
620
621
622
623
	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
624
		ptr, (unsigned long)size);
Bob Halley's avatar
base  
Bob Halley committed
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
	__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
641
642

#endif /* ISC_MEMCLUSTER_LEGACY */
Michael Graff's avatar
Michael Graff committed
643
644
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


/*
 * 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
671
		item = item->next;
Michael Graff's avatar
Michael Graff committed
672
673
674
675
676
677
678
679
680
		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
681
		next = item->next;
Michael Graff's avatar
Michael Graff committed
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
		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
711
		next = item->next;
Michael Graff's avatar
Michael Graff committed
712
713
714
715
716
717
718
719
		mem_putunlocked(mctx, item, mpctx->size);
		INSIST(mpctx->freecount > 0);
		mpctx->freecount--;
		item = next;
	} while (item != NULL);
}

isc_result_t
Michael Graff's avatar
Michael Graff committed
720
isc_mempool_create(isc_mem_t *mctx, size_t size,
Michael Graff's avatar
Michael Graff committed
721
722
723
724
725
		   isc_mempool_t **mpctxp)
{
	isc_mempool_t *mpctx;

	REQUIRE(VALID_CONTEXT(mctx));
Michael Graff's avatar
Michael Graff committed
726
	REQUIRE(size > 0);
Michael Graff's avatar
Michael Graff committed
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
	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);

	mpctx = mem_getunlocked(mctx, sizeof(isc_mempool_t));
	if (mpctx == NULL) {
		UNLOCK(&mctx->lock);
		return (ISC_R_NOMEMORY);
	}

	mpctx->magic = MEMPOOL_MAGIC;
	mpctx->mctx = mctx;
Michael Graff's avatar
Michael Graff committed
743
	mpctx->size = size;
Michael Graff's avatar
Michael Graff committed
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
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
843
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
886
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
927
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
	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;

	mem_putunlocked(mpctx->mctx, mpctx, sizeof(isc_mempool_t));

	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++) {
		item = mem_getunlocked(mctx, mpctx->size);
		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) {
		__isc_mem_put(mctx, mem, mpctx->size);
		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);
}