1 /* This file is concerned with allocating and freeing arbitrary-size blocks of
8 #include <minix/callnr.h>
9 #include <minix/type.h>
10 #include <minix/config.h>
11 #include <minix/const.h>
12 #include <minix/sysutil.h>
13 #include <minix/syslib.h>
14 #include <minix/debug.h>
15 #include <minix/bitmap.h>
29 #include "sanitycheck.h"
32 /* Number of physical pages in a 32-bit address space */
33 #define NUMBER_PHYSICAL_PAGES (0x100000000ULL/VM_PAGE_SIZE)
34 #define PAGE_BITMAP_CHUNKS BITMAP_CHUNKS(NUMBER_PHYSICAL_PAGES)
35 static bitchunk_t free_pages_bitmap
[PAGE_BITMAP_CHUNKS
];
36 #define PAGE_CACHE_MAX 10000
37 static int free_page_cache
[PAGE_CACHE_MAX
];
38 static int free_page_cache_size
= 0;
40 /* Used for sanity check. */
41 static phys_bytes mem_low
, mem_high
;
43 static void free_pages(phys_bytes addr
, int pages
);
44 static phys_bytes
alloc_pages(int pages
, int flags
);
51 } pagemap
[NUMBER_PHYSICAL_PAGES
];
54 #define page_isfree(i) GET_BIT(free_pages_bitmap, i)
56 /*===========================================================================*
58 *===========================================================================*/
59 phys_clicks
alloc_mem(phys_clicks clicks
, u32_t memflags
)
61 /* Allocate a block of memory from the free list using first fit. The block
62 * consists of a sequence of contiguous bytes, whose length in clicks is
63 * given by 'clicks'. A pointer to the block is returned. The block is
64 * always on a click boundary. This procedure is called when memory is
65 * needed for FORK or EXEC.
67 phys_clicks mem
= NO_MEM
, align_clicks
= 0;
69 if(memflags
& PAF_ALIGN64K
) {
70 align_clicks
= (64 * 1024) / CLICK_SIZE
;
71 clicks
+= align_clicks
;
72 } else if(memflags
& PAF_ALIGN16K
) {
73 align_clicks
= (16 * 1024) / CLICK_SIZE
;
74 clicks
+= align_clicks
;
77 mem
= alloc_pages(clicks
, memflags
);
79 free_yielded(clicks
* CLICK_SIZE
);
80 mem
= alloc_pages(clicks
, memflags
);
88 o
= mem
% align_clicks
;
100 /*===========================================================================*
102 *===========================================================================*/
103 void free_mem(phys_clicks base
, phys_clicks clicks
)
105 /* Return a block of free memory to the hole list. The parameters tell where
106 * the block starts in physical memory and how big it is. The block is added
107 * to the hole list. If it is contiguous with an existing hole on either end,
108 * it is merged with the hole or holes.
110 if (clicks
== 0) return;
112 assert(CLICK_SIZE
== VM_PAGE_SIZE
);
113 free_pages(base
, clicks
);
117 /*===========================================================================*
119 *===========================================================================*/
120 void mem_init(chunks
)
121 struct memory
*chunks
; /* list of free memory chunks */
123 /* Initialize hole lists. There are two lists: 'hole_head' points to a linked
124 * list of all the holes (unused memory) in the system; 'free_slots' points to
125 * a linked list of table entries that are not in use. Initially, the former
126 * list has one entry for each chunk of physical memory, and the second
127 * list links together the remaining table slots. As memory becomes more
128 * fragmented in the course of time (i.e., the initial big holes break up into
129 * smaller holes), new table slots are needed to represent them. These slots
130 * are taken from the list headed by 'free_slots'.
136 memset(free_pages_bitmap
, 0, sizeof(free_pages_bitmap
));
138 /* Use the chunks of physical memory to allocate holes. */
139 for (i
=NR_MEMS
-1; i
>=0; i
--) {
140 if (chunks
[i
].size
> 0) {
141 phys_bytes from
= CLICK2ABS(chunks
[i
].base
),
142 to
= CLICK2ABS(chunks
[i
].base
+chunks
[i
].size
)-1;
143 if(first
|| from
< mem_low
) mem_low
= from
;
144 if(first
|| to
> mem_high
) mem_high
= to
;
145 free_mem(chunks
[i
].base
, chunks
[i
].size
);
146 total_pages
+= chunks
[i
].size
;
153 void mem_sanitycheck(char *file
, int line
)
156 for(i
= 0; i
< NUMBER_PHYSICAL_PAGES
; i
++) {
157 if(!page_isfree(i
)) continue;
158 MYASSERT(usedpages_add(i
* VM_PAGE_SIZE
, VM_PAGE_SIZE
) == OK
);
163 void memstats(int *nodes
, int *pages
, int *largest
)
170 for(i
= 0; i
< NUMBER_PHYSICAL_PAGES
; i
++) {
172 while(i
< NUMBER_PHYSICAL_PAGES
&& page_isfree(i
)) {
176 if(size
== 0) continue;
184 static int findbit(int low
, int startscan
, int pages
, int memflags
, int *len
)
186 int run_length
= 0, i
, freerange_start
;
188 for(i
= startscan
; i
>= low
; i
--) {
189 if(!page_isfree(i
)) {
191 int chunk
= i
/BITCHUNK_BITS
, moved
= 0;
195 !MAP_CHUNK(free_pages_bitmap
, chunk
*BITCHUNK_BITS
)) {
199 if(moved
) { i
= chunk
* BITCHUNK_BITS
+ BITCHUNK_BITS
; }
202 if(!run_length
) { freerange_start
= i
; run_length
= 1; }
203 else { freerange_start
--; run_length
++; }
204 assert(run_length
<= pages
);
205 if(run_length
== pages
) {
206 /* good block found! */
208 return freerange_start
;
215 /*===========================================================================*
217 *===========================================================================*/
218 static phys_bytes
alloc_pages(int pages
, int memflags
)
220 phys_bytes boundary16
= 16 * 1024 * 1024 / VM_PAGE_SIZE
;
221 phys_bytes boundary1
= 1 * 1024 * 1024 / VM_PAGE_SIZE
;
222 phys_bytes mem
= NO_MEM
;
223 int maxpage
= NUMBER_PHYSICAL_PAGES
- 1, i
;
224 static int lastscan
= -1;
225 int startscan
, run_length
;
227 if(memflags
& PAF_LOWER16MB
)
228 maxpage
= boundary16
- 1;
229 else if(memflags
& PAF_LOWER1MB
)
230 maxpage
= boundary1
- 1;
232 /* no position restrictions: check page cache */
234 while(free_page_cache_size
> 0) {
235 i
= free_page_cache
[free_page_cache_size
-1];
237 free_page_cache_size
--;
239 assert(mem
!= NO_MEM
);
243 free_page_cache_size
--;
248 if(lastscan
< maxpage
&& lastscan
>= 0)
249 startscan
= lastscan
;
250 else startscan
= maxpage
;
253 mem
= findbit(0, startscan
, pages
, memflags
, &run_length
);
255 mem
= findbit(0, maxpage
, pages
, memflags
, &run_length
);
259 /* remember for next time */
262 for(i
= mem
; i
< mem
+ pages
; i
++) {
263 UNSET_BIT(free_pages_bitmap
, i
);
266 if(memflags
& PAF_CLEAR
) {
268 if ((s
= sys_memset(NONE
, 0, CLICK_SIZE
*mem
,
269 VM_PAGE_SIZE
*pages
)) != OK
)
270 panic("alloc_mem: sys_memset failed: %d", s
);
276 /*===========================================================================*
278 *===========================================================================*/
279 static void free_pages(phys_bytes pageno
, int npages
)
281 int i
, lim
= pageno
+ npages
- 1;
284 if(sys_memset(NONE
, 0xa5a5a5a5, VM_PAGE_SIZE
* pageno
,
285 VM_PAGE_SIZE
* npages
) != OK
)
286 panic("free_pages: sys_memset failed");
289 for(i
= pageno
; i
<= lim
; i
++) {
290 SET_BIT(free_pages_bitmap
, i
);
291 if(free_page_cache_size
< PAGE_CACHE_MAX
) {
292 free_page_cache
[free_page_cache_size
++] = i
;
297 /*===========================================================================*
299 *===========================================================================*/
300 void printmemstats(void)
302 int nodes
, pages
, largest
;
303 memstats(&nodes
, &pages
, &largest
);
304 printf("%d blocks, %d pages (%lukB) free, largest %d pages (%lukB)\n",
305 nodes
, pages
, (unsigned long) pages
* (VM_PAGE_SIZE
/1024),
306 largest
, (unsigned long) largest
* (VM_PAGE_SIZE
/1024));
312 /*===========================================================================*
314 *===========================================================================*/
315 void usedpages_reset(void)
317 memset(pagemap
, 0, sizeof(pagemap
));
320 /*===========================================================================*
322 *===========================================================================*/
323 int usedpages_add_f(phys_bytes addr
, phys_bytes len
, char *file
, int line
)
325 u32_t pagestart
, pages
;
330 assert(!(addr
% VM_PAGE_SIZE
));
331 assert(!(len
% VM_PAGE_SIZE
));
334 pagestart
= addr
/ VM_PAGE_SIZE
;
335 pages
= len
/ VM_PAGE_SIZE
;
339 assert(pagestart
> 0);
340 assert(pagestart
< NUMBER_PHYSICAL_PAGES
);
341 thisaddr
= pagestart
* VM_PAGE_SIZE
;
342 assert(pagestart
>= 0);
343 assert(pagestart
< NUMBER_PHYSICAL_PAGES
);
344 if(pagemap
[pagestart
].used
) {
345 static int warnings
= 0;
347 printf("%s:%d: usedpages_add: addr 0x%lx reused, first %s:%d\n",
348 file
, line
, thisaddr
, pagemap
[pagestart
].file
, pagemap
[pagestart
].line
);
352 pagemap
[pagestart
].used
= 1;
353 pagemap
[pagestart
].file
= file
;
354 pagemap
[pagestart
].line
= line
;
364 /*===========================================================================*
365 * alloc_mem_in_list *
366 *===========================================================================*/
367 struct memlist
*alloc_mem_in_list(phys_bytes bytes
, u32_t flags
, phys_bytes known
)
369 phys_bytes rempages
, phys_count
;
370 struct memlist
*head
= NULL
, *tail
= NULL
;
373 assert(!(bytes
% VM_PAGE_SIZE
));
375 rempages
= bytes
/ VM_PAGE_SIZE
;
377 assert(!(flags
& PAF_CONTIG
));
379 if(known
!= MAP_NONE
)
388 if(known
== MAP_NONE
) {
389 mem
= alloc_pages(1, flags
);
392 freed
= free_yielded(rempages
* VM_PAGE_SIZE
);
394 assert(mem
!= MAP_NONE
);
396 mem
= ABS2CLICK(phys_count
);
397 phys_count
+= VM_PAGE_SIZE
;
398 assert(mem
!= MAP_NONE
);
399 assert(mem
!= NO_MEM
);
401 assert(mem
!= MAP_NONE
);
402 } while(mem
== NO_MEM
&& freed
> 0);
405 printf("alloc_mem_in_list: giving up, %lukB missing\n",
406 rempages
* VM_PAGE_SIZE
/1024);
408 free_mem_list(head
, 1);
412 if(!(SLABALLOC(ml
))) {
413 free_mem_list(head
, 1);
414 free_pages(mem
, VM_PAGE_SIZE
);
418 USE(ml
, ml
->phys
= CLICK2ABS(mem
); ml
->next
= NULL
;);
427 } while(rempages
> 0);
431 for(ml
= head
; ml
; ml
= ml
->next
) {
434 if(!(flags
& PAF_CONTIG
)) {
436 assert(ml
->phys
+ ml
->length
!= ml
->next
->phys
);
445 /*===========================================================================*
447 *===========================================================================*/
448 void free_mem_list(struct memlist
*list
, int all
)
451 struct memlist
*next
;
453 assert(!(list
->phys
% VM_PAGE_SIZE
));
455 free_pages(list
->phys
/ VM_PAGE_SIZE
, 1);
461 /*===========================================================================*
463 *===========================================================================*/
464 void print_mem_list(struct memlist
*list
)
467 printf("0x%lx-0x%lx", list
->phys
, list
->phys
+VM_PAGE_SIZE
-1);