7af11563795e925884ecc56269bf5eb8e5950f00
[pintos-anon] / src / threads / malloc.c
1 #include "malloc.h"
2 #include <stdint.h>
3 #include "debug.h"
4 #include "lib.h"
5 #include "list.h"
6 #include "synch.h"
7 #include "mmu.h"
8 #include "palloc.h"
9
10 /* A simple implementation of malloc().
11
12    The size of each request, in bytes, is rounded up to a power
13    of 2 and assigned to the "descriptor" that manages blocks of
14    that size.  The descriptor keeps a list of free blocks.  If
15    the free list is nonempty, one of its blocks is used to
16    satisfy the request.
17
18    Otherwise, a new page of memory, called an "arena", is
19    obtained from the page allocator (if none is available,
20    malloc() returns a null pointer).  The new arena is divided
21    into blocks, all of which are added to the descriptor's free
22    list.  Then we return one of the new blocks.
23
24    When we free a block, we add it to its descriptor's free list.
25    But if the arena that the block was in now has no in-use
26    blocks, we remove all of the arena's blocks from the free list
27    and give the arena back to the page allocator.
28
29    Major limitation: the largest block that can be allocated is
30    PGSIZE / 2, or 2 kB.  Use palloc_get() to allocate pages (4 kB
31    blocks).  You're on your own if you need more memory than
32    that. */
33
34 /* Descriptor. */
35 struct desc
36   {
37     size_t block_size;          /* Size of each element in bytes. */
38     size_t blocks_per_arena;    /* Number of blocks in an arena. */
39     struct list free_list;      /* List of free blocks. */
40     struct lock lock;           /* Lock. */
41   };
42
43 /* Arena. */
44 struct arena 
45   {
46     struct desc *desc;          /* Owning descriptor. */
47     size_t free_cnt;            /* Number of free blocks. */
48   };
49
50 /* Free block. */
51 struct block 
52   {
53     list_elem free_elem;        /* Free list element. */
54   };
55
56 /* Our set of descriptors. */
57 static struct desc descs[16];   /* Descriptors. */
58 static size_t desc_cnt;         /* Number of descriptors. */
59
60 static struct arena *block_to_arena (struct block *);
61 static struct block *arena_to_block (struct arena *, size_t idx);
62
63 /* Initializes the malloc() descriptors. */
64 void
65 malloc_init (void) 
66 {
67   size_t block_size;
68
69   for (block_size = 16; block_size < PGSIZE; block_size *= 2)
70     {
71       struct desc *d = &descs[desc_cnt++];
72       ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
73       d->block_size = block_size;
74       d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
75       list_init (&d->free_list);
76       lock_init (&d->lock, "malloc");
77     }
78 }
79
80 /* Obtains and returns a new block of at least SIZE bytes.
81    Returns a null pointer if memory is not available. */
82 void *
83 malloc (size_t size) 
84 {
85   struct desc *d;
86   struct block *b;
87   struct arena *a;
88
89   /* A null pointer satisfies a request for 0 bytes. */
90   if (size == 0)
91     return NULL;
92
93   /* Find the smallest descriptor that satisfies a SIZE-byte
94      request. */
95   for (d = descs; d < descs + desc_cnt; d++)
96     if (size < d->block_size)
97       break;
98   if (d == descs + desc_cnt) 
99     {
100       printk ("malloc: %zu byte allocation too big\n", size);
101       return NULL; 
102     }
103
104   lock_acquire (&d->lock);
105
106   /* If the free list is empty, create a new arena. */
107   if (list_empty (&d->free_list))
108     {
109       size_t i;
110
111       /* Allocate a page. */
112       a = palloc_get (0);
113       if (a == NULL) 
114         {
115           lock_release (&d->lock);
116           return NULL; 
117         }
118
119       /* Initialize arena and add its blocks to the free list. */
120       a->desc = d;
121       a->free_cnt = d->blocks_per_arena;
122       for (i = 0; i < d->blocks_per_arena; i++) 
123         {
124           b = arena_to_block (a, i);
125           list_push_back (&d->free_list, &b->free_elem);
126         }
127     }
128
129   /* Get a block from free list and return it. */
130   b = list_entry (list_pop_front (&d->free_list), struct block, free_elem);
131   a = block_to_arena (b);
132   a->free_cnt--;
133   lock_release (&d->lock);
134   return b;
135 }
136
137 /* Allocates and return A times B bytes initialized to zeroes.
138    Returns a null pointer if memory is not available. */
139 void *
140 calloc (size_t a, size_t b) 
141 {
142   void *p;
143   size_t size;
144
145   /* Calculate block size. */
146   size = a * b;
147   if (size < a || size < b)
148     return NULL;
149
150   /* Allocate and zero memory. */
151   p = malloc (size);
152   if (p != NULL)
153     memset (p, 0, size);
154
155   return p;
156 }
157
158 /* Frees block P, which must have been previously allocated with
159    malloc() or calloc(). */
160 void
161 free (void *p) 
162 {
163   struct block *b = p;
164   struct arena *a = block_to_arena (b);
165   struct desc *d = a->desc;
166
167   if (p == NULL)
168     return;
169
170   lock_acquire (&d->lock);
171
172   /* Add block to free list. */
173   list_push_front (&d->free_list, &b->free_elem);
174
175   /* If the arena is now entirely unused, free it. */
176   if (++a->free_cnt >= d->blocks_per_arena) 
177     {
178       size_t i;
179
180       ASSERT (a->free_cnt == d->blocks_per_arena);
181       for (i = 0; i < d->blocks_per_arena; i++) 
182         {
183           struct block *b = arena_to_block (a, i);
184           list_remove (&b->free_elem);
185         }
186       palloc_free (a);
187     }
188
189   lock_release (&d->lock);
190 }
191 \f
192 /* Returns the arena that block B is inside. */
193 static struct arena *
194 block_to_arena (struct block *b)
195 {
196   return pg_round_down (b);
197 }
198
199 /* Returns the (IDX - 1)'th block within arena A. */
200 static struct block *
201 arena_to_block (struct arena *a, size_t idx) 
202 {
203   ASSERT (idx < a->desc->blocks_per_arena);
204   return (struct block *) ((uint8_t *) a
205                            + sizeof *a
206                            + idx * a->desc->block_size);
207 }