|
|
1.1 root 1: /* $Header: /src386/STREAMS/coh.386/RCS/alloc.c,v 2.3 93/08/09 13:35:06 bin Exp Locker: bin $ */
2: /* (lgl-
3: * The information contained herein is a trade secret of Mark Williams
4: * Company, and is confidential information. It is provided under a
5: * license agreement, and may be copied or disclosed only under the
6: * terms of that agreement. Any reproduction or disclosure of this
7: * material without the express written authorization of Mark Williams
8: * Company or persuant to the license agreement is unlawful.
9: *
10: * COHERENT Version 2.3.37
11: * Copyright (c) 1982, 1983, 1984.
12: * An unpublished work by Mark Williams Company, Chicago.
13: * All rights reserved.
14: -lgl) */
15: /*
16: * Coherent.
17: * Storage allocator.
18: *
19: * $Log: alloc.c,v $
20: * Revision 2.3 93/08/09 13:35:06 bin
21: * Kernel 82 changes
22: *
23: * Revision 2.2 93/07/26 14:28:19 nigel
24: * Nigel's R80
25: *
26: * Revision 1.4 93/04/14 10:06:13 root
27: * r75
28: *
29: * Revision 1.2 92/01/06 11:58:31 hal
30: * Compile with cc.mwc.
31: *
32: * Revision 1.1 88/03/24 16:13:25 src
33: * Initial revision
34: *
35: */
36:
37: #include <common/ccompat.h>
38: #include <common/__parith.h>
39: #include <common/_tricks.h>
40: #include <kernel/param.h>
41: #include <sys/debug.h>
42:
43: #include <sys/coherent.h>
44: #include <sys/errno.h>
45: #include <sys/proc.h>
46:
47: #include <kernel/alloc.h>
48:
49: /*
50: * Alloc definitions. These used to be in <sys/machine.h> for some unknown
51: * and unknowable reason. They belong here, so now here they are. Since the
52: * person(s) who wrote this stuff neglected to mention what the alignment
53: * issues are, we'll stay with what they did.
54: *
55: * This stuff is aligned on double-byte boundaries and the pointer to the
56: * next block in a circular list is tagged with the status of the current
57: * block. Blocks are not coalesced when freed, that is done by the allocator
58: * when trying to locate a sufficiently large free block.
59: *
60: * As an extra twist, you might have wondered why alloc () tries to loop
61: * twice over the whole arena. It does that because it looks for an exact fit
62: * (after coalescing). The allocator has no memory because there is no actual
63: * overall arena structure, so every call to alloc () will try to coalesce the
64: * entire arena unless there is an exact-sized hole.
65: */
66:
67: enum {
68: BLOCK_FREE = 0,
69: BLOCK_USED
70: };
71:
72: #define ALIGN_MASK 1
73: #define align(p) ((ALL *) ((__ptr_arith_t) (p) & ~ ALIGN_MASK))
74: #define link(p) align ((p)->a_link)
75: #define tstfree(p) (((p)->a_link & BLOCK_USED) == BLOCK_FREE)
76:
77: #define MAKE_LINK(a,f) ((__ptr_arith_t) a + (f))
78: #define MAKE_FREE(a) ((a)->a_link &= ~ BLOCK_USED)
79: #define MAKE_USED(a) ((a)->a_link |= BLOCK_USED)
80:
81:
82: typedef union all_u {
83: __ptr_arith_t a_link;
84: } ALL;
85:
86:
87: #define NEXT_FIT 1
88:
89: #if NEXT_FIT
90:
91: struct _heap {
92: ALL * _next_block;
93: };
94:
95: #define HEAP_CONTROL_SIZE sizeof (heap_t)
96: #define START_BLOCK(heap) ((heap)->_next_block)
97: #define SET_START_BLOCK(heap,newstart) \
98: ((heap)->_next_block = (newstart))
99: #else
100:
101: #define HEAP_CONTROL_SIZE 0
102: #define START_BLOCK(heap) ((ALL *) (heap))
103: #define SET_START_BLOCK(heap,newstart) ((void) 0)
104:
105: #endif
106:
107: #ifndef TEST /* Do not test setarena() or alloc() or free(). */
108:
109: /*
110: * Create an arena.
111: */
112:
113: heap_t *
114: setarena(cp, n)
115: register char *cp;
116: {
117: ALL * first_block;
118: ALL * last_block;
119: heap_t * heap_control;
120:
121: /*
122: * Begin by aligning the memory passed in and rounding down the size.
123: */
124:
125: {
126: int align = (__ptr_arith_t) cp & ALIGN_MASK;
127:
128: if (align) {
129: align = ALIGN_MASK + 1 - align;
130: cp += align;
131: n -= align;
132: }
133:
134: n &= ~ sizeof (ALL *);
135: }
136:
137: /*
138: * Make room for a heap control area.
139: */
140:
141: heap_control = (heap_t *) cp;
142:
143: cp += HEAP_CONTROL_SIZE;
144: n -= HEAP_CONTROL_SIZE;
145:
146: first_block = (ALL *) cp;
147: if ((last_block = (ALL *) (cp + n) - 1) < first_block)
148: panic("Arena %x too small", (int) cp);
149:
150: /*
151: * The initial memory arena consists of a circular list of blocks,
152: * one large free block and one tiny used block at the end. In the
153: * original "design", there was no heap control block.
154: */
155:
156: first_block->a_link = MAKE_LINK (last_block, BLOCK_FREE);
157: last_block->a_link = MAKE_LINK (first_block, BLOCK_USED);
158:
159: SET_START_BLOCK (heap_control, first_block);
160: return heap_control;
161: }
162:
163:
164: #if 0
165: /*
166: * NIGEL: This code intrigues me... let's keep statistics.
167: */
168:
169: typedef unsigned long stat_t;
170:
171: static stat_t _allocations;
172: static stat_t _block_tests;
173: static stat_t _block_fits;
174: static stat_t _exact_fits;
175:
176: #define ADD_STAT(stat) ((stat += 1) == 0 ? stat -- : 0)
177:
178: void dumpstats () {
179: printf ("allocations = %d\ntotal tests = %d\n"
180: "total matches = %d\nexact fits = %d\n",
181: _allocations, _block_tests, _block_fits, _exact_fits);
182: }
183: #else
184: # define ADD_STAT(stat) ((void) 0)
185: #endif
186:
187: /*
188: * Allocate `l' bytes of memory.
189: */
190:
191: __VOID__ *
192: alloc (heap_control, size)
193: heap_t * heap_control;
194: size_t size;
195: {
196: register ALL *scan_block;
197: register ALL *next_block;
198: register unsigned i;
199: register unsigned n;
200: register unsigned s;
201:
202: ADD_STAT (_allocations);
203:
204: n = 1 + __DIVIDE_ROUNDUP (size, sizeof (ALL));
205:
206: #if EXACT_FIT
207: for (i = 0 ; i < 2 ; i ++) {
208: #endif
209: for (scan_block = START_BLOCK (heap_control) ;
210: link (scan_block) != START_BLOCK (heap_control) ;
211: scan_block = link (scan_block)) {
212: ASSERT (vtop (scan_block) != NULL);
213: ADD_STAT (_block_tests);
214:
215: if (! tstfree (scan_block))
216: continue;
217:
218: for (next_block = link (scan_block) ;
219: tstfree (next_block) ;
220: next_block = link (next_block))
221: if (next_block == START_BLOCK (heap_control))
222: break;
223:
224: scan_block->a_link = MAKE_LINK (next_block,
225: BLOCK_FREE);
226: if ((s = next_block - scan_block) < n)
227: continue;
228:
229: ADD_STAT (_block_fits);
230:
231: if (s > n) {
232: #if EXACT_FIT
233: /*
234: * This innocent-looking line of code is what makes this system prefer
235: * exact fits (which only happen about 10% of the time from the
236: * statistics which I have collected).
237: */
238: if (i == 0)
239: continue;
240: #endif
241: (scan_block + n)->a_link =
242: MAKE_LINK (next_block, BLOCK_FREE);
243: next_block = scan_block + n;
244: scan_block->a_link = MAKE_LINK (next_block,
245: BLOCK_FREE);
246: }
247: MAKE_USED (scan_block);
248: SET_START_BLOCK (heap_control, next_block);
249: #if 0
250: memset (scan_block + 1, 0, size);
251: #endif
252: #if EXACT_FIT
253: if (i == 0)
254: ADD_STAT (_exact_fits);
255: #endif
256: return (__VOID__ *) (scan_block + 1);
257: }
258: #if EXACT_FIT
259: }
260: #endif
261: u.u_error = ENOSPC;
262: return NULL;
263: }
264:
265: /*
266: * Free memory.
267: */
268: free(cp)
269: char *cp;
270: {
271: register ALL *ap;
272: extern char __end;
273:
274: #if 0
275: ap = ((ALL *)cp) - 1;
276: if (ap<(ALL *)&__end || tstfree(ap))
277: panic("Bad free %x\n", (unsigned)cp);
278: #else
279: ap = ((ALL *)cp) - 1;
280: if (ap<(ALL *)&__end) {
281: int *r = (int *)(&cp); /* return address */
282: printf("cp=%x ap=%x &__end=%x\n", cp, ap, &__end);
283: panic("Bad free() from eip=%x\n", *(r-1));
284: }
285: if (tstfree(ap)) {
286: int *r = (int *)(&cp); /* return address */
287: printf("cp=%x tstfree(%x)=%x\n", cp, ap, tstfree(ap));
288: panic("Bad free() from eip=%x\n", *(r-1));
289: }
290: #endif
291: MAKE_FREE (ap);
292: }
293:
294: #endif /* TEST */
295:
296: #ifdef _I386
297: /*
298: * unsigned char *palloc(int size);
299: *
300: * Allocate 'size' bytes of kernel space, which does not cross a click
301: * boundary. Returns a pointer to the space allocated on success,
302: * NULL on failure.
303: *
304: * Allocate twice as much memory as we need, and then return a chunk that
305: * does not cross a click boundary. Immediately before the chunk that
306: * we return, we store the true address of the chunk that was kalloc()'d.
307: *
308: * Since this routine is for relatively small short-lived objects,
309: * which we expect to allocate frequently, speed is more important than
310: * space overhead.
311: *
312: * We assume that kalloc() returns word aligned addresses.
313: *
314: * There are two cases:
315: * There is enough room before the click boundary (or there is no click
316: * boundary) for the pointer and the memory we need.
317: * Otherwise, return the chunk starting at the click boundary, storing
318: * the pointer right before the click boundary. This trick allows
319: * us to allocate up to 1 full click.
320: *
321: * If kalloc() did NOT return word aligned chunks, then there would be
322: * a third case, where there might not be enough space for the pointer
323: * before the click boundary.
324: */
325:
326: #define c_boundry(x) ctob(btoc((x)+1)) /* Next click boundary above x. */
327: #define VOID unsigned char
328:
329: #ifdef TEST
330: #undef kalloc
331: #undef kfree
332: VOID *kalloc();
333: void kfree();
334: #endif /* TEST */
335:
336: VOID *
337: palloc(size)
338: int size; /* Size in bytes of area to allocate. */
339: {
340: VOID *local_arena; /* Value returned by kalloc(). */
341: VOID *boundry; /* Next click boundry above local_arena. */
342: VOID *retval; /* What we give back to our caller. */
343:
344: if (size > NBPC)
345: panic("palloc(%x): can not palloc more than 1 click.", size);
346:
347: /* Fetch twice as much space as requested, plus a pointer. */
348: if ((local_arena = (VOID *) kalloc (sizeof (VOID *) + (2 * size)))
349: == NULL)
350: return NULL;
351:
352: boundry = (VOID *) c_boundry (local_arena);
353:
354: T_PIGGY(0x2000, printf("b: %x ", boundry));
355:
356: /* First case: enough space before the boundry. */
357: if ( (boundry - local_arena) >= (size + sizeof(VOID *)) ) {
358:
359: T_PIGGY(0x2000, printf("c1 "));
360:
361: * (VOID **)local_arena = local_arena;
362: retval = local_arena + sizeof(VOID *);
363: } else if ((boundry - local_arena) < sizeof(VOID *)) {
364: /*
365: * Second case: There is not enough space before the
366: * boundry for the whole pointer.
367: */
368: T_PIGGY(0x2000, printf("c2 "));
369:
370: * (VOID **)local_arena = local_arena;
371: retval = local_arena + sizeof(VOID *);
372: } else {
373:
374: T_PIGGY(0x2000, printf("c3: %x ", (boundry - local_arena)));
375:
376: * (VOID **)(boundry - sizeof(VOID *)) = local_arena;
377: retval = boundry;
378: }
379:
380: T_PIGGY( 0x2000,
381: printf("palloc(%x) = %x:%x (was %x:%x), ",
382: size, retval, (retval+size)-1,
383: local_arena, (local_arena+(2*size)+sizeof(VOID *))-1)
384: );
385:
386: #if 0
387: /*
388: * NIGEL: Things in trace macros must now be expressions. These ones
389: * weren't worth cleaning up.
390: */
391: T_PIGGY( 0x2000,
392: if ((retval+size)-1 > (local_arena+(2*size)+sizeof(VOID *))-1) {
393: printf("\npalloc() overrun\n");
394: }
395: if (retval < local_arena) {
396: printf("\npalloc() underrun\n");
397: }
398: );
399: #endif
400:
401: return (VOID *) retval;
402: } /* palloc() */
403:
404: /*
405: * void pfree(VOID *ptr);
406: * Free the chunk of memory 'ptr' allocated by palloc().
407: *
408: * Note that 'ptr' is really a VOID *, but we call it VOID **
409: * to simplify arithmetic.
410: *
411: * The address returned by kalloc() is stored immediately
412: * before the chunk returned by palloc().
413: */
414: void
415: pfree(ptr)
416: VOID *ptr[];
417: {
418: T_PIGGY(0x2000, printf("pfree(%x):kfree(%x), ", ptr, *(ptr-1)));
419: kfree(*(ptr-1));
420: } /* pfree() */
421:
422:
423: #ifdef TEST
424:
425: #include <sys/compat.h>
426: #include <stdio.h>
427: #include <stdarg.h>
428:
429: #define FOURK 4096 /* How many bytes in 4K? */
430: #define NUM_TESTS 40 /* How many tests do we run? */
431: #define SMALL_NUMBER 6 /* A small number whose exact value we don't care about. */
432: #define HUGE (100*FOURK) /* Allocate from this pool. */
433: #define IGNORE(v) (v==v) /* Lint food. */
434:
435: unsigned t_piggy = 0x2000; /* Turn on TRACER bits. */
436:
437: main()
438: {
439: int i;
440: VOID *chunk;
441:
442: for (i = 0; i < NUM_TESTS; ++i) {
443: if (NULL == (chunk = palloc(SMALL_NUMBER))) {
444: printf("No more fake memory to eat.\n");
445: printf("This is probably a bug.\n");
446: exit(1);
447: }
448:
449: printf("chunk: %x\n", chunk);
450: }
451: } /* main() for TEST */
452:
453: /*
454: * Print a message and die.
455: */
456:
457: panic(format)
458: char * format;
459: {
460: va_list args;
461: va_start (args, format);
462: vprintf (format, args);
463: va_end (args);
464: exit(1);
465: }
466:
467: /*
468: * Fake kalloc() for use by palloc().
469: * Allocate a chunk of some non-existant memory space.
470: */
471: VOID *
472: kalloc(size)
473: int size;
474: {
475: static VOID *base = NULL;
476: static VOID *top_free = NULL;
477: VOID *retval;
478:
479:
480: /*
481: * First time through, allocate a nice big chunk of memory
482: * to carve up.
483: */
484: if (NULL == base) {
485: if (NULL == (base = malloc(HUGE))) {
486: printf("Can not malloc %d bytes.\n", HUGE);
487: exit(1);
488: }
489: /* Make sure we start close to a click boundry. */
490: top_free = c_boundry(base) + SMALL_NUMBER;
491: }
492:
493: retval = top_free;
494: /*
495: * We want to encourage test addresses to migrate accross
496: * click boundries.
497: */
498: if (size < (FOURK - 1)) {
499: top_free += (FOURK - 1);
500: } else {
501: top_free += size;
502: }
503:
504: return(retval);
505: } /* kalloc() */
506:
507: /*
508: * Fake kfree for pfree() to use.
509: */
510: void
511: kfree(addr)
512: VOID *addr;
513: {
514: IGNORE(addr);
515: /* Do nothing! */
516: } /* kfree() */
517:
518: #endif /* TEST */
519:
520: #endif /* _I386 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.