home *** CD-ROM | disk | FTP | other *** search
- head 1.2;
- access;
- symbols
- version39-41:1.1;
- locks;
- comment @ * @;
-
-
- 1.2
- date 92.09.14.01.40.24; author mwild; state Exp;
- branches;
- next 1.1;
-
- 1.1
- date 92.05.22.01.42.33; author mwild; state Exp;
- branches;
- next ;
-
-
- desc
- @buddy allocator, replaces AllocMem()/FreeMem() calls in [k]malloc.c
- @
-
-
- 1.2
- log
- @change from using aligned blocks (obtained thru an AllocMem/FreeMem/AllocAbs
- hack) to using non-aligned blocks. The price for this is an additional
- field in every allocated block.
-
- In the same run, change Forbid/Permit into Semaphore locking.
- @
- text
- @/*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: buddy-alloc.c,v 1.1 1992/05/22 01:42:33 mwild Exp $
- *
- * $Log: buddy-alloc.c,v $
- * Revision 1.1 1992/05/22 01:42:33 mwild
- * Initial revision
- *
- */
- #define KERNEL
- #include "ixemul.h"
- #include <exec/memory.h>
- #include <stddef.h>
-
- #undef DEBUG
-
- #ifdef TEST
- #ifndef PRETTY_STABLE
- #define AllocMem(size,attr) test_allocmem(size,attr)
- #define AllocAbs(size,buf) test_allocabs(size,buf)
- #define FreeMem(buf,size) test_freemem(buf,size)
- #define Forbid()
- #define Permit()
- #define ix_panic(str) puts(str)
- #define P(arg) printf arg
- #else
- #define P(arg)
- #define ix_panic(arg)
- #endif
- #else
- #ifdef DEBUG
- #define P(arg) kprintf arg
- #else
- #define P(arg)
- #endif
- #endif
-
- /* this provides a straight replacement for AllocMem() and FreeMem().
- Being this, it does *not* remember the size of allocation, the
- clients have to do this instead. */
-
- /* NOTE: currently only two pools are supported, MEMF_PUBLIC and
- ! MEMF_PUBLIC. No MEMF_CHIP pools are needed by the library
- and are thus not supported */
-
-
-
-
- /* TUNING: The two parameters that can be adjusted to fine tune
- allocation strategy are MAXSIZE and BUDDY_LIMIT. By setting
- MAXSIZE larger than BUDDY_LIMIT results in less Exec
- overhead, since blocks stay longer in the buddy system.
- Setting MAXSIZE==BUDDY_LIMIT sets memory usage to the
- minimum, at the cost of more Exec calls. */
-
-
- /* no request for memory can be lower than this */
- #define MINLOG2 3
- #define MINSIZE (1 << MINLOG2)
-
- /* this is the size the buddy system gets memory pieces from Exec */
- #define MAXLOG2 15 /* get 32K chunks */
- #define MAXSIZE (1 << MAXLOG2)
-
- /* this is the limit for b_alloc to go straight to Exec */
- #define BUDDY_LIMIT (1 << (MAXLOG2 - 5)) /* but serve only upto 1K */
-
-
-
- #define PRIVATE_POOL 0
- #define PUBLIC_POOL 1
- #define NUMPOOLS 2 /* public and !public */
-
- struct free_list {
- u_int exec_attr;
- struct SignalSemaphore sem;
- struct MinList buckets[MAXLOG2 - MINLOG2];
- } free_list[NUMPOOLS] = { { 0, }, { MEMF_PUBLIC, } };
-
-
- struct free_block {
- /* if head is 0, the block is free, and index indicates its size */
- struct free_list *head;
- /* this is the price we pay for allowing non-aligned blocks. Each block
- allocated with this buddy system has to carry the information along
- to which block originally allocated by Exec it belongs. */
- void *exec_block;
-
- /* from here on, fields only exist while the block is on the free list.
- The application sees a block as a chunk of memory starting at &next */
- struct free_block *next, *prev; /* min-node compatible */
- int index;
- };
-
-
- void
- init_buddy (void)
- {
- int i, l;
-
- for (l = 0; l < NUMPOOLS; l++)
- {
- for (i = 0; i < MAXLOG2 - MINLOG2; i++)
- NewList ((struct List *) &free_list[l].buckets[i]);
-
- InitSemaphore (&free_list[l].sem);
- }
- }
-
- static inline struct free_block *
- unlink_block (struct free_list *fl, u_char ind, void *block)
- {
- struct free_block *fb = (struct free_block *) block;
-
- if (! fb)
- {
- fb = (struct free_block *) RemHead ((struct List *) &fl->buckets[ind]);
- if (fb)
- {
- fb = (struct free_block *) ((int)fb - offsetof (struct free_block, next));
- fb->head = fl;
- P((" unlink_block (%s, %ld) == $%lx\n",
- fl == free_list ? "PRIVATE" : (fl == free_list + 1 ? "PUBLIC" : "BOGOUS"), ind, fb));
-
- }
- }
- else
- {
- P((" unlink_block (%s, %ld, $%lx)\n",
- fl == free_list ? "PRIVATE" : (fl == free_list + 1 ? "PUBLIC" : "BOGOUS"), ind, block));
-
- fb->head = fl; /* free later into this pool */
- Remove ((struct Node *) &fb->next);
- }
-
- return fb;
- }
-
- #if defined(DEBUG) || defined(TEST)
- void
- check_empty (int pool)
- {
- int i;
- struct free_block *fb;
-
- for (i = 0; i < MAXLOG2 - MINLOG2; i++)
- {
- while (fb = unlink_block (free_list + pool, i, 0))
- {
- P(("%s POOL: $%lx block @@$%lx\n",
- pool ? "PUBLIC" : "PRIVATE", (1 << (i + MINLOG2)), fb));
- }
- }
- }
- #endif
-
- static void inline
- link_block (struct free_list *fl, u_char ind, void *block)
- {
- struct free_block *fb = (struct free_block *) block;
-
- P((" link_block (%s, %ld, $%lx)\n",
- fl == free_list ? "PRIVATE" : (fl == free_list + 1 ? "PUBLIC" : "BOGOUS"), ind, block));
-
- fb->head = 0; /* we're on the freelist of this pool */
- fb->index = ind; /* and of this size */
- AddHead ((struct List *) &fl->buckets[ind], (struct Node *) &fb->next);
- }
-
- /* this is a very special log2() function that knows the upper bound
- of its argument, and also automatically rounds to the next upper
- power of two */
-
- static inline int const
- log2 (int size)
- {
- int pow = MAXLOG2;
- int lower_bound = 1 << (MAXLOG2 - 1);
-
- for (;;)
- {
- if (size > lower_bound)
- return pow;
-
- lower_bound >>= 1;
- pow--;
- }
- }
-
-
- static inline struct free_block *
- get_block (struct free_list *fl, u_char index)
- {
- struct free_block *fb;
- struct free_block *buddy;
-
- P((" get_block (%s, %ld)\n",
- fl == free_list ? "PRIVATE" : (fl == free_list + 1 ? "PUBLIC" : "BOGOUS"), index));
-
- #ifdef DEBUG
- if (!fl || (fl != free_list && fl != free_list + 1))
- {
- ix_panic ("get_block: illegal free list!!");
- return;
- }
- #endif
-
- if (index == (MAXLOG2 - MINLOG2))
- {
- fb = (struct free_block *) AllocMem (MAXSIZE, fl->exec_attr);
- if (! fb)
- return 0;
-
- fb->exec_block = fb; /* buddies are relative to this base address */
- fb->head = fl; /* not free */
-
- return fb;
- }
- else if (fb = unlink_block (fl, index, 0))
- return fb;
-
- fb = get_block (fl, index + 1);
-
- if (fb)
- {
- buddy = (struct free_block *)
- ((((int)fb - (int)fb->exec_block) | (1 << (index + MINLOG2)))
- + (int)fb->exec_block);
-
- #ifdef DEBUG
- if (fb == buddy)
- ix_panic ("get_block: | operator should be ^ !!");
- #endif
-
- /* it is only when splitting where we have to propagate the exec
- block address (verify if you like ;-)) */
- buddy->exec_block = fb->exec_block;
- link_block (fl, index, buddy);
- }
-
- return fb;
- }
-
-
- static inline void
- free_block (struct free_list *fl, u_char index, struct free_block *fb)
- {
- struct free_block *buddy;
-
- P((" free_block (%s, %ld, $%lx)\n",
- fl == free_list ? "PRIVATE" : (fl == free_list + 1 ? "PUBLIC" : "BOGOUS"), index, fb));
-
- #ifdef DEBUG
- if (! fb)
- {
- ix_panic ("free_block: zero block!!");
- return;
- }
-
- if (!fl || (fl != free_list && fl != free_list + 1))
- {
- ix_panic ("free_block: illegal free list in block!!");
- return;
- }
- #endif
-
- buddy = (struct free_block *)
- ((((int)fb - (int)fb->exec_block) ^ (1 << (index + MINLOG2)))
- + (int)fb->exec_block);
-
- if (index == (MAXLOG2 - MINLOG2))
- {
- #ifdef DEBUG
- if (fb != fb->exec_block)
- ix_panic ("free_block: fb != fb->exec_block!");
- #endif
- FreeMem (fb, MAXSIZE);
- return;
- }
- else if (buddy->head || buddy->index != index)
- {
- /* too bad, buddy is not on freelist or of wrong size */
- link_block (fl, index, fb);
- return;
- }
-
- /* reserve the buddy, then recombine both */
- unlink_block (fl, index, buddy);
-
- /* since the buddy is free as well, recombine both blocks
- and free the twice as large block */
- free_block (fl, index + 1, fb < buddy ? fb : buddy);
- }
-
-
- void *
- b_alloc (int size, unsigned pool)
- {
- u_char bucket;
- struct free_block *block;
- struct free_list *fl;
-
- #ifdef DEBUG
- if (pool >= NUMPOOLS)
- {
- ix_panic ("b_alloc: illegal pool spezified!");
- return 0;
- }
- #endif
-
- if (size < MINSIZE)
- size = MINSIZE;
-
- /* the additional bytes are needed for the freelist pointer at
- the beginning of each block in use and the base block originally
- obtained from Exec. */
-
- if (size > BUDDY_LIMIT - offsetof (struct free_block, next))
- return AllocMem (size, pool == PUBLIC_POOL ? MEMF_PUBLIC : 0);
-
- size += offsetof (struct free_block, next);
-
- bucket = log2 (size) - MINLOG2;
- fl = free_list + pool;
-
- /* this once was Forbid/Permit. We're much nicer now ;-)) */
- ObtainSemaphore (&fl->sem);
- block = get_block (fl, bucket);
- ReleaseSemaphore (&fl->sem);
-
- #ifdef DEBUG
- if (!block)
- ix_panic ("b_alloc: out of memory");
- #endif
-
- P(("b_alloc ($%lx, %ld) = $%lx\n", size, pool, block));
-
- if (block)
- return (void *) & block->next;
- else
- return block;
- }
-
-
- void
- b_free (struct free_block *fb, int size)
- {
- u_char bucket;
- struct free_list *fl;
-
- if (size < MINSIZE)
- size = MINSIZE;
-
- if (size > BUDDY_LIMIT - offsetof (struct free_block, next))
- {
- FreeMem ((void *) fb, size);
- return;
- }
-
- size += offsetof (struct free_block, next);
-
- bucket = log2 (size) - MINLOG2;
- fb = (struct free_block *) ((int)fb - offsetof (struct free_block, next));
- fl = fb->head;
-
- P(("b_free ($%lx, $%lx)\n", fb, size));
-
- /* this once was Forbid/Permit. We're much nicer now ;-)) */
- ObtainSemaphore (&fl->sem);
- free_block (fl, bucket, fb);
- ReleaseSemaphore (&fl->sem);
- }
-
-
- #ifdef TEST
- int
- main (int argc, char *argv[])
- {
- #ifndef PRETTY_STABLE
- int i;
- void *allocs[16], *bllocs[13];
-
- init_buddy ();
-
- printf ("13 allocs of pool 0\n");
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- {
- allocs[i] = b_alloc ((i + 3) * (i + 3), 0);
- memset (allocs[i], 0x34, (i + 3) * (i + 3));
- }
-
- printf ("13 allocs of pool 1\n");
- for (i = 0; i < sizeof (bllocs)/sizeof (void*); i++)
- {
- bllocs[i] = b_alloc ((i + 3) * (i + 3) * (i + 3), 1);
- memset (bllocs[i], 0x34, (i + 3) * (i + 3) * (i + 3));
- }
-
- printf ("13 frees of pool 1\n");
- for (i = sizeof (bllocs)/sizeof (void*) - 1; i >= 0 ; i--)
- b_free (bllocs[i], (i + 3) * (i + 3) * (i + 3));
-
- printf ("13 frees of pool 0\n");
- for (i = sizeof (allocs)/sizeof (void*) - 1; i >= 0 ; i--)
- b_free (allocs[i], (i + 3) * (i + 3));
-
-
- printf ("13 allocs of pool 0\n");
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- {
- allocs[i] = b_alloc ((i + 3) * (i + 3), 0);
- memset (allocs[i], 0x34, (i + 3) * (i + 3));
- }
-
- printf ("13 allocs of pool 1\n");
- for (i = 0; i < sizeof (bllocs)/sizeof (void*); i++)
- {
- bllocs[i] = b_alloc ((i + 3) * (i + 3) * (i + 3), 1);
- memset (bllocs[i], 0x34, (i + 3) * (i + 3) * (i + 3));
- }
-
- printf ("13 frees of pool 1\n");
- for (i = sizeof (bllocs)/sizeof (void*) - 1; i >= 0 ; i--)
- b_free (bllocs[i], (i + 3) * (i + 3) * (i + 3));
-
- printf ("13 frees of pool 0\n");
- for (i = sizeof (allocs)/sizeof (void*) - 1; i >= 0 ; i--)
- b_free (allocs[i], (i + 3) * (i + 3));
- #else
- /* run a performance comparison */
- void *allocs[10000];
- struct timeval tstart, tend, tdiff1, tdiff2;
- int size;
- int i;
-
- init_buddy ();
-
- if (argc <= 1)
- size = 50;
- else
- size = atoi (argv[1]) ? : 50;
-
- #if 1
- gettimeofday (&tstart, 0);
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- {
- allocs[i] = b_alloc (size, 0);
- memset (allocs[i], 0x34, size);
- }
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- b_free (allocs[i], size);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- {
- allocs[i] = b_alloc (size, 0);
- memset (allocs[i], 0x34, size);
- }
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- b_free (allocs[i], size);
-
- #if 0
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- allocs[i] = b_alloc (size, 0);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- b_free (allocs[i], size);
- #endif
- gettimeofday (&tend, 0);
- tdiff1.tv_sec = tend.tv_sec - tstart.tv_sec;
- tdiff1.tv_usec = tend.tv_usec -tstart.tv_usec;
- if (tdiff1.tv_usec < 0)
- {
- tdiff1.tv_usec += 1000000;
- tdiff1.tv_sec--;
- }
- #endif
-
- #if 0
- gettimeofday (&tstart, 0);
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- allocs[i] = AllocMem (size, 0);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- FreeMem (allocs[i], size);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- allocs[i] = AllocMem (size, 0);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- FreeMem (allocs[i], size);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- allocs[i] = AllocMem (size, 0);
-
- for (i = 0; i < sizeof (allocs)/sizeof (void*); i++)
- FreeMem (allocs[i], size);
- gettimeofday (&tend, 0);
- tdiff2.tv_sec = tend.tv_sec - tstart.tv_sec;
- tdiff2.tv_usec = tend.tv_usec -tstart.tv_usec;
- if (tdiff2.tv_usec < 0)
- {
- tdiff2.tv_usec += 1000000;
- tdiff2.tv_sec--;
- }
- #endif
-
- check_empty (0);
-
- #if 1
- printf ("delta with buddy system: %d.%06d\n", tdiff1.tv_sec, tdiff1.tv_usec);
- #endif
- #if 0
- printf ("delta without buddy system: %d.%06d\n", tdiff2.tv_sec, tdiff2.tv_usec);
- #endif
- #endif
- }
-
- #ifndef PRETTY_STABLE
- test_allocmem (int size, int attr)
- {
- int res = malloc (size);
- printf ("AllocMem ($%lx, %d) = $%lx\n", size, attr, res);
- return res;
- }
-
- test_freemem (void *buf, int size)
- {
- printf ("FreeMem ($%lx, $%lx)\n", buf, size);
- }
-
- test_allocabs (int size, void *buf)
- {
- return buf;
- }
- #endif
- #endif
- @
-
-
- 1.1
- log
- @Initial revision
- @
- text
- @d19 1
- a19 1
- * $Id$
- d21 4
- a24 1
- * $Log$
- d82 1
- a82 1
- #define BUDDY_LIMIT (1 << (MAXLOG2 - 3)) /* but serve only upto 4K */
- d92 1
- d100 7
- a107 2
- /* index is only used while the block is on the free list. When
- allocated, this place is in use by the application */
- d115 6
- a120 4
- int i;
-
- for (i = 0; i < MAXLOG2 - MINLOG2; i++)
- NewList ((struct List *) &free_list[0].buckets[i]);
- d122 2
- a123 2
- for (i = 0; i < MAXLOG2 - MINLOG2; i++)
- NewList ((struct List *) &free_list[1].buckets[i]);
- d226 1
- a226 2
- /* remember we are inside Forbid! */
- fb = (struct free_block *) AllocMem (2 * MAXSIZE, fl->exec_attr);
- d229 3
- a231 7
- FreeMem ((void *) fb, 2 * MAXSIZE);
- /* still in Forbid, so we still own the block ! Get a maximal
- aligned block out of the twice as large block */
- fb = (struct free_block *)
- AllocAbs (MAXSIZE, (void *)(((int)fb + MAXSIZE - 1) & -MAXSIZE));
- if (fb)
- fb->head = fl; /* not free */
- a241 1
- /* having aligned blocks GREATLY simplifies handling of buddies.. */
- d243 2
- a244 1
- ((int)fb | (1 << (index + MINLOG2)));
- d251 3
- d264 1
- a264 2
- struct free_block *buddy = (struct free_block *)
- ((int)fb ^ (1 << (index + MINLOG2)));
- d283 4
- d289 4
- d308 1
- a308 2
- free_block (fl, index + 1,
- (struct free_block *) ((int)fb & ~(1 << (index + MINLOG2))));
- d330 3
- a332 2
- /* the additional 4 bytes are needed for the freelist pointer at
- the beginning of each block in use */
- d334 1
- a334 1
- if (size >= BUDDY_LIMIT - offsetof (struct free_block, next))
- d341 3
- a343 1
- Forbid ();
- d345 2
- a346 2
- Permit ();
-
- d370 1
- a370 1
- if (size >= BUDDY_LIMIT - offsetof (struct free_block, next))
- d384 2
- a385 1
- Forbid();
- d387 1
- a387 1
- Permit();
- @
-