6 /* In kalloc, a *core* is a large chunk of contiguous memory. Each core is
7 * associated with a master header, which keeps the size of the current core
8 * and the pointer to next core. Kalloc allocates small *blocks* of memory from
9 * the cores and organizes free memory blocks in a circular single-linked list.
11 * In the following diagram, "@" stands for the header of a free block (of type
12 * header_t), "#" for the header of an allocated block (of type size_t), "-"
13 * for free memory, and "+" for allocated memory.
15 * master This region is core 1. master This region is core 2.
17 * *@-------#++++++#++++++++++++@-------- *@----------#++++++++++++#+++++++@------------
19 * p=p->ptr->ptr->ptr->ptr p->ptr p->ptr->ptr p->ptr->ptr->ptr
22 #define MIN_CORE_SIZE 0x80000
24 typedef struct header_t
{
30 header_t base
, *loop_head
, *core_head
; /* base is a zero-sized block always kept in the loop */
33 static void panic(const char *s
)
35 fprintf(stderr
, "%s\n", s
);
41 return calloc(1, sizeof(kmem_t
));
44 void km_destroy(void *_km
)
46 kmem_t
*km
= (kmem_t
*)_km
;
48 if (km
== NULL
) return;
49 for (p
= km
->core_head
; p
!= NULL
;) {
57 static header_t
*morecore(kmem_t
*km
, size_t nu
)
61 nu
= (nu
+ 1 + (MIN_CORE_SIZE
- 1)) / MIN_CORE_SIZE
* MIN_CORE_SIZE
; /* the first +1 for core header */
62 bytes
= nu
* sizeof(header_t
);
63 q
= (header_t
*)malloc(bytes
);
64 if (!q
) panic("[morecore] insufficient memory");
65 q
->ptr
= km
->core_head
, q
->size
= nu
, km
->core_head
= q
;
67 *p
= nu
- 1; /* the size of the free block; -1 because the first unit is used for the core header */
68 kfree(km
, p
+ 1); /* initialize the new "core"; NB: the core header is not looped. */
72 void kfree(void *_km
, void *ap
) /* kfree() also adds a new core to the circular list */
75 kmem_t
*km
= (kmem_t
*)_km
;
82 p
= (header_t
*)((size_t*)ap
- 1);
83 p
->size
= *((size_t*)ap
- 1);
84 /* Find the pointer that points to the block to be freed. The following loop can stop on two conditions:
86 * a) "p>q && p<q->ptr": @------#++++++++#+++++++@------- @---------------#+++++++@-------
87 * (can also be in | | | -> | |
88 * two cores) q p q->ptr q q->ptr
90 * @-------- #+++++++++@-------- @-------- @------------------
94 * b) "q>=q->ptr && (p>q || p<q->ptr)": @-------#+++++ @--------#+++++++ @-------#+++++ @----------------
98 * #+++++++@----- #++++++++@------- @------------- #++++++++@-------
100 * p q->ptr q q->ptr q
102 for (q
= km
->loop_head
; !(p
> q
&& p
< q
->ptr
); q
= q
->ptr
)
103 if (q
>= q
->ptr
&& (p
> q
|| p
< q
->ptr
)) break;
104 if (p
+ p
->size
== q
->ptr
) { /* two adjacent blocks, merge p and q->ptr (the 2nd and 4th cases) */
105 p
->size
+= q
->ptr
->size
;
106 p
->ptr
= q
->ptr
->ptr
;
107 } else if (p
+ p
->size
> q
->ptr
&& q
->ptr
>= p
) {
108 panic("[kfree] The end of the allocated block enters a free block.");
109 } else p
->ptr
= q
->ptr
; /* backup q->ptr */
111 if (q
+ q
->size
== p
) { /* two adjacent blocks, merge q and p (the other two cases) */
115 } else if (q
+ q
->size
> p
&& p
>= q
) {
116 panic("[kfree] The end of a free block enters the allocated block.");
117 } else km
->loop_head
= p
, q
->ptr
= p
; /* in two cores, cannot be merged; create a new block in the list */
120 void *kmalloc(void *_km
, size_t n_bytes
)
122 kmem_t
*km
= (kmem_t
*)_km
;
126 if (n_bytes
== 0) return 0;
127 if (km
== NULL
) return malloc(n_bytes
);
128 n_units
= (n_bytes
+ sizeof(size_t) + sizeof(header_t
) - 1) / sizeof(header_t
) + 1;
130 if (!(q
= km
->loop_head
)) /* the first time when kmalloc() is called, intialize it */
131 q
= km
->loop_head
= km
->base
.ptr
= &km
->base
;
132 for (p
= q
->ptr
;; q
= p
, p
= p
->ptr
) { /* search for a suitable block */
133 if (p
->size
>= n_units
) { /* p->size if the size of current block. This line means the current block is large enough. */
134 if (p
->size
== n_units
) q
->ptr
= p
->ptr
; /* no need to split the block */
135 else { /* split the block. NB: memory is allocated at the end of the block! */
136 p
->size
-= n_units
; /* reduce the size of the free block */
137 p
+= p
->size
; /* p points to the allocated block */
138 *(size_t*)p
= n_units
; /* set the size */
140 km
->loop_head
= q
; /* set the end of chain */
141 return (size_t*)p
+ 1;
143 if (p
== km
->loop_head
) { /* then ask for more "cores" */
144 if ((p
= morecore(km
, n_units
)) == 0) return 0;
149 void *kcalloc(void *_km
, size_t count
, size_t size
)
151 kmem_t
*km
= (kmem_t
*)_km
;
153 if (size
== 0 || count
== 0) return 0;
154 if (km
== NULL
) return calloc(count
, size
);
155 p
= kmalloc(km
, count
* size
);
156 memset(p
, 0, count
* size
);
160 void *krealloc(void *_km
, void *ap
, size_t n_bytes
) // TODO: this can be made more efficient in principle
162 kmem_t
*km
= (kmem_t
*)_km
;
163 size_t n_units
, *p
, *q
;
166 kfree(km
, ap
); return 0;
168 if (km
== NULL
) return realloc(ap
, n_bytes
);
169 if (ap
== NULL
) return kmalloc(km
, n_bytes
);
170 n_units
= (n_bytes
+ sizeof(size_t) + sizeof(header_t
) - 1) / sizeof(header_t
);
172 if (*p
>= n_units
) return ap
; /* TODO: this prevents shrinking */
173 q
= (size_t*)kmalloc(km
, n_bytes
);
174 memcpy(q
, ap
, (*p
- 1) * sizeof(header_t
));
179 void km_stat(const void *_km
, km_stat_t
*s
)
181 kmem_t
*km
= (kmem_t
*)_km
;
183 memset(s
, 0, sizeof(km_stat_t
));
184 if (km
== NULL
|| km
->loop_head
== NULL
) return;
185 for (p
= km
->loop_head
;; p
= p
->ptr
) {
186 s
->available
+= p
->size
* sizeof(header_t
);
187 if (p
->size
!= 0) ++s
->n_blocks
; /* &kmem_t::base is always one of the cores. It is zero-sized. */
188 if (p
->ptr
> p
&& p
+ p
->size
> p
->ptr
)
189 panic("[km_stat] The end of a free block enters another free block.");
190 if (p
->ptr
== km
->loop_head
) break;
192 for (p
= km
->core_head
; p
!= NULL
; p
= p
->ptr
) {
193 size_t size
= p
->size
* sizeof(header_t
);
196 s
->largest
= s
->largest
> size
? s
->largest
: size
;