#include <stdint.h>
#include "debug.h"
#include "lib.h"
+#include "list.h"
+#include "synch.h"
#include "mmu.h"
#include "palloc.h"
+/* A simple implementation of malloc().
+
+ The size of each request, in bytes, is rounded up to a power
+ of 2 and assigned to the "descriptor" that manages blocks of
+ that size. The descriptor keeps a list of free blocks. If
+ the free list is nonempty, one of its blocks is used to
+ satisfy the request.
+
+ Otherwise, a new page of memory, called an "arena", is
+ obtained from the page allocator (if none is available,
+ malloc() returns a null pointer). The new arena is divided
+ into blocks, all of which are added to the descriptor's free
+ list. Then we return one of the new blocks.
+
+ When we free a block, we add it to its descriptor's free list.
+ But if the arena that the block was in now has no in-use
+ blocks, we remove all of the arena's blocks from the free list
+ and give the arena back to the page allocator.
+
+ Major limitation: the largest block that can be allocated is
+ PGSIZE / 2, or 2 kB. Use palloc_get() to allocate pages (4 kB
+ blocks). You're on your own if you need more memory than
+ that. */
+
+/* Descriptor. */
struct desc
{
- size_t slot_size; /* Size of each element in bytes. */
- struct slot *free_list; /* Unused slots. */
- struct arena *arenas; /* Arenas. */
+ size_t block_size; /* Size of each element in bytes. */
+ size_t blocks_per_arena; /* Number of blocks in an arena. */
+ struct list free_list; /* List of free blocks. */
+ struct lock lock; /* Lock. */
};
+/* Arena. */
struct arena
{
struct desc *desc; /* Owning descriptor. */
- struct arena *prev, *next; /* Doubly linked list of arenas. */
+ size_t free_cnt; /* Number of free blocks. */
};
-struct slot
+/* Free block. */
+struct block
{
- struct slot *next; /* Singly linked list of slots. */
+ list_elem free_elem; /* Free list element. */
};
-struct desc descs[16];
-size_t desc_cnt;
+/* Our set of descriptors. */
+static struct desc descs[16]; /* Descriptors. */
+static size_t desc_cnt; /* Number of descriptors. */
+
+static struct arena *block_to_arena (struct block *);
+static struct block *arena_to_block (struct arena *, size_t idx);
+/* Initializes the malloc() descriptors. */
void
malloc_init (void)
{
- size_t slot_size;
+ size_t block_size;
- for (slot_size = 16; slot_size < PGSIZE; slot_size *= 2)
+ for (block_size = 16; block_size < PGSIZE; block_size *= 2)
{
struct desc *d = &descs[desc_cnt++];
ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
- d->slot_size = slot_size;
- d->free_list = NULL;
- d->arenas = NULL;
+ d->block_size = block_size;
+ d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
+ list_init (&d->free_list);
+ lock_init (&d->lock, "malloc");
}
}
-static struct arena *
-slot_to_arena (struct slot *s)
-{
- return (struct arena *) ((uint32_t) s & ~(PGSIZE - 1));
-}
-
-static void *
-get_free_slot (struct desc *d)
-{
- void *block = d->free_list;
- ASSERT (block != NULL);
- d->free_list = d->free_list->next;
- return block;
-}
-
+/* Obtains and returns a new block of at least SIZE bytes.
+ Returns a null pointer if memory is not available. */
void *
malloc (size_t size)
{
struct desc *d;
+ struct block *b;
struct arena *a;
- size_t ofs;
+ /* A null pointer satisfies a request for 0 bytes. */
if (size == 0)
return NULL;
+ /* Find the smallest descriptor that satisfies a SIZE-byte
+ request. */
for (d = descs; d < descs + desc_cnt; d++)
- if (size < d->slot_size)
+ if (size < d->block_size)
break;
if (d == descs + desc_cnt)
{
printk ("malloc: %zu byte allocation too big\n", size);
return NULL;
}
-
- if (d->free_list != NULL)
- return get_free_slot (d);
- a = palloc_get (0);
- if (a == NULL)
- return NULL;
+ lock_acquire (&d->lock);
- a->desc = d;
- a->prev = NULL;
- a->next = d->arenas;
- if (d->arenas != NULL)
- d->arenas->prev = a;
- for (ofs = sizeof *a; ofs + d->slot_size <= PGSIZE; ofs += d->slot_size)
+ /* If the free list is empty, create a new arena. */
+ if (list_empty (&d->free_list))
{
- struct slot *s = (struct slot *) ((uint8_t *) a + ofs);
- s->next = d->free_list;
- d->free_list = s;
+ size_t i;
+
+ /* Allocate a page. */
+ a = palloc_get (0);
+ if (a == NULL)
+ {
+ lock_release (&d->lock);
+ return NULL;
+ }
+
+ /* Initialize arena and add its blocks to the free list. */
+ a->desc = d;
+ a->free_cnt = d->blocks_per_arena;
+ for (i = 0; i < d->blocks_per_arena; i++)
+ {
+ b = arena_to_block (a, i);
+ list_push_back (&d->free_list, &b->free_elem);
+ }
}
- return get_free_slot (d);
+ /* Get a block from free list and return it. */
+ b = list_entry (list_pop_front (&d->free_list), struct block, free_elem);
+ a = block_to_arena (b);
+ a->free_cnt--;
+ lock_release (&d->lock);
+ return b;
}
+/* Allocates and return A times B bytes initialized to zeroes.
+ Returns a null pointer if memory is not available. */
void *
calloc (size_t a, size_t b)
{
void *p;
size_t size;
+ /* Calculate block size. */
size = a * b;
if (size < a || size < b)
return NULL;
+ /* Allocate and zero memory. */
p = malloc (size);
if (p != NULL)
memset (p, 0, size);
return p;
}
+/* Frees block P, which must have been previously allocated with
+ malloc() or calloc(). */
void
free (void *p)
{
- struct slot *s = p;
- struct arena *a = slot_to_arena (s);
+ struct block *b = p;
+ struct arena *a = block_to_arena (b);
struct desc *d = a->desc;
- s->next = d->free_list;
- d->free_list = s;
+ lock_acquire (&d->lock);
+
+ /* Add block to free list. */
+ list_push_front (&d->free_list, &b->free_elem);
+
+ /* If the arena is now entirely unused, free it. */
+ if (++a->free_cnt >= d->blocks_per_arena)
+ {
+ size_t i;
+
+ ASSERT (a->free_cnt == d->blocks_per_arena);
+ for (i = 0; i < d->blocks_per_arena; i++)
+ {
+ struct block *b = arena_to_block (a, i);
+ list_remove (&b->free_elem);
+ }
+ palloc_free (a);
+ }
+
+ lock_release (&d->lock);
+}
+\f
+/* Returns the arena that block B is inside. */
+static struct arena *
+block_to_arena (struct block *b)
+{
+ return pg_round_down (b);
+}
+
+/* Returns the (IDX - 1)'th block within arena A. */
+static struct block *
+arena_to_block (struct arena *a, size_t idx)
+{
+ ASSERT (idx < a->desc->blocks_per_arena);
+ return (struct block *) ((uint8_t *) a
+ + sizeof *a
+ + idx * a->desc->block_size);
}