xref: /minix/minix/servers/vm/alloc.c (revision 7f5f010b)
1 /* This file is concerned with allocating and freeing arbitrary-size blocks of
2  * physical memory.
3  */
4 
5 #define _SYSTEM 1
6 
7 #include <minix/com.h>
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>
16 
17 #include <sys/mman.h>
18 
19 #include <limits.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <assert.h>
23 #include <memory.h>
24 
25 #include "vm.h"
26 #include "proto.h"
27 #include "util.h"
28 #include "glo.h"
29 #include "sanitycheck.h"
30 #include "memlist.h"
31 
32 /* Number of physical pages in a 32-bit address space */
33 #define NUMBER_PHYSICAL_PAGES (int)(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;
39 
40 /* Used for sanity check. */
41 static phys_bytes mem_low, mem_high;
42 
43 static void free_pages(phys_bytes addr, int pages);
44 static phys_bytes alloc_pages(int pages, int flags);
45 
46 #if SANITYCHECKS
47 struct {
48 	int used;
49 	const char *file;
50 	int line;
51 } pagemap[NUMBER_PHYSICAL_PAGES];
52 #endif
53 
54 #define page_isfree(i) GET_BIT(free_pages_bitmap, i)
55 
56 #define RESERVEDMAGIC		0x6e4c74d5
57 #define MAXRESERVEDPAGES	300
58 #define MAXRESERVEDQUEUES	 15
59 
60 static struct reserved_pages {
61 	struct reserved_pages *next;	/* next in use */
62 	int max_available;	/* queue depth use, 0 if not in use at all */
63 	int npages;		/* number of consecutive pages */
64 	int mappedin;		/* must reserved pages also be mapped? */
65 	int n_available;	/* number of queue entries */
66 	int allocflags;		/* allocflags for alloc_mem */
67 	struct reserved_pageslot {
68 		phys_bytes	phys;
69 		void		*vir;
70 	} slots[MAXRESERVEDPAGES];
71 	u32_t magic;
72 } reservedqueues[MAXRESERVEDQUEUES], *first_reserved_inuse = NULL;
73 
74 int missing_spares = 0;
75 
76 static void sanitycheck_queues(void)
77 {
78 	struct reserved_pages *mrq;
79 	int m = 0;
80 
81 	for(mrq = first_reserved_inuse; mrq; mrq = mrq->next) {
82 		assert(mrq->max_available > 0);
83 		assert(mrq->max_available >= mrq->n_available);
84 		m += mrq->max_available - mrq->n_available;
85 	}
86 
87 	assert(m == missing_spares);
88 }
89 
90 static void sanitycheck_rq(struct reserved_pages *rq)
91 {
92 	assert(rq->magic == RESERVEDMAGIC);
93 	assert(rq->n_available >= 0);
94 	assert(rq->n_available <= MAXRESERVEDPAGES);
95 	assert(rq->n_available <= rq->max_available);
96 
97 	sanitycheck_queues();
98 }
99 
100 void *reservedqueue_new(int max_available, int npages, int mapped, int allocflags)
101 {
102 	int r;
103 	struct reserved_pages *rq;
104 
105 	assert(max_available > 0);
106 	assert(max_available < MAXRESERVEDPAGES);
107 	assert(npages > 0);
108 	assert(npages < 10);
109 
110 	for(r = 0; r < MAXRESERVEDQUEUES; r++)
111 		if(!reservedqueues[r].max_available)
112 			break;
113 
114 	if(r >= MAXRESERVEDQUEUES) {
115 		printf("VM: %d reserved queues in use\n", MAXRESERVEDQUEUES);
116 		return NULL;
117 	}
118 
119 	rq = &reservedqueues[r];
120 
121 	memset(rq, 0, sizeof(*rq));
122 	rq->next = first_reserved_inuse;
123 	first_reserved_inuse = rq;
124 
125 	rq->max_available = max_available;
126 	rq->npages = npages;
127 	rq->mappedin = mapped;
128 	rq->allocflags = allocflags;
129 	rq->magic = RESERVEDMAGIC;
130 
131 	missing_spares += max_available;
132 
133 	return rq;
134 }
135 
136 static void
137 reservedqueue_fillslot(struct reserved_pages *rq,
138 	struct reserved_pageslot *rps, phys_bytes ph, void *vir)
139 {
140 	rps->phys = ph;
141 	rps->vir = vir;
142 	assert(missing_spares > 0);
143 	if(rq->mappedin) assert(vir);
144 	missing_spares--;
145 	rq->n_available++;
146 }
147 
148 static int
149 reservedqueue_addslot(struct reserved_pages *rq)
150 {
151 	phys_bytes cl, cl_addr;
152 	void *vir;
153 	struct reserved_pageslot *rps;
154 
155 	sanitycheck_rq(rq);
156 
157 	if((cl = alloc_mem(rq->npages, rq->allocflags)) == NO_MEM)
158 		return ENOMEM;
159 
160 	cl_addr = CLICK2ABS(cl);
161 
162 	vir = NULL;
163 
164 	if(rq->mappedin) {
165 		if(!(vir = vm_mappages(cl_addr, rq->npages))) {
166 			free_mem(cl, rq->npages);
167 			printf("reservedqueue_addslot: vm_mappages failed\n");
168 			return ENOMEM;
169 		}
170 	}
171 
172 	rps = &rq->slots[rq->n_available];
173 
174 	reservedqueue_fillslot(rq, rps, cl_addr, vir);
175 
176 	return OK;
177 }
178 
179 void reservedqueue_add(void *rq_v, void *vir, phys_bytes ph)
180 {
181 	struct reserved_pages *rq = rq_v;
182 	struct reserved_pageslot *rps;
183 
184 	sanitycheck_rq(rq);
185 
186 	rps = &rq->slots[rq->n_available];
187 
188 	reservedqueue_fillslot(rq, rps, ph, vir);
189 }
190 
191 static int reservedqueue_fill(void *rq_v)
192 {
193 	struct reserved_pages *rq = rq_v;
194 	int r;
195 
196 	sanitycheck_rq(rq);
197 
198 	while(rq->n_available < rq->max_available)
199 		if((r=reservedqueue_addslot(rq)) != OK)
200 			return r;
201 
202 	return OK;
203 }
204 
205 int
206 reservedqueue_alloc(void *rq_v, phys_bytes *ph, void **vir)
207 {
208 	struct reserved_pages *rq = rq_v;
209 	struct reserved_pageslot *rps;
210 
211 	sanitycheck_rq(rq);
212 
213 	if(rq->n_available < 1) return ENOMEM;
214 
215 	rq->n_available--;
216 	missing_spares++;
217 	rps = &rq->slots[rq->n_available];
218 
219 	*ph = rps->phys;
220 	*vir = rps->vir;
221 
222 	sanitycheck_rq(rq);
223 
224 	return OK;
225 }
226 
227 void alloc_cycle(void)
228 {
229 	struct reserved_pages *rq;
230 	sanitycheck_queues();
231 	for(rq = first_reserved_inuse; rq && missing_spares > 0; rq = rq->next) {
232 		sanitycheck_rq(rq);
233 		reservedqueue_fill(rq);
234 		sanitycheck_rq(rq);
235 	}
236 	sanitycheck_queues();
237 }
238 
239 /*===========================================================================*
240  *				alloc_mem				     *
241  *===========================================================================*/
242 phys_clicks alloc_mem(phys_clicks clicks, u32_t memflags)
243 {
244 /* Allocate a block of memory from the free list using first fit. The block
245  * consists of a sequence of contiguous bytes, whose length in clicks is
246  * given by 'clicks'.  A pointer to the block is returned.  The block is
247  * always on a click boundary.  This procedure is called when memory is
248  * needed for FORK or EXEC.
249  */
250   phys_clicks mem = NO_MEM, align_clicks = 0;
251 
252   if(memflags & PAF_ALIGN64K) {
253   	align_clicks = (64 * 1024) / CLICK_SIZE;
254 	clicks += align_clicks;
255   } else if(memflags & PAF_ALIGN16K) {
256 	align_clicks = (16 * 1024) / CLICK_SIZE;
257 	clicks += align_clicks;
258   }
259 
260   do {
261 	mem = alloc_pages(clicks, memflags);
262   } while(mem == NO_MEM && cache_freepages(clicks) > 0);
263 
264   if(mem == NO_MEM)
265   	return mem;
266 
267   if(align_clicks) {
268   	phys_clicks o;
269   	o = mem % align_clicks;
270   	if(o > 0) {
271   		phys_clicks e;
272   		e = align_clicks - o;
273 	  	free_mem(mem, e);
274 	  	mem += e;
275 	}
276   }
277 
278   return mem;
279 }
280 
281 void mem_add_total_pages(int pages)
282 {
283 	total_pages += pages;
284 }
285 
286 /*===========================================================================*
287  *				free_mem				     *
288  *===========================================================================*/
289 void free_mem(phys_clicks base, phys_clicks clicks)
290 {
291 /* Return a block of free memory to the hole list.  The parameters tell where
292  * the block starts in physical memory and how big it is.  The block is added
293  * to the hole list.  If it is contiguous with an existing hole on either end,
294  * it is merged with the hole or holes.
295  */
296   if (clicks == 0) return;
297 
298   assert(CLICK_SIZE == VM_PAGE_SIZE);
299   free_pages(base, clicks);
300   return;
301 }
302 
303 /*===========================================================================*
304  *				mem_init				     *
305  *===========================================================================*/
306 void mem_init(struct memory *chunks)
307 {
308 /* Initialize hole lists.  There are two lists: 'hole_head' points to a linked
309  * list of all the holes (unused memory) in the system; 'free_slots' points to
310  * a linked list of table entries that are not in use.  Initially, the former
311  * list has one entry for each chunk of physical memory, and the second
312  * list links together the remaining table slots.  As memory becomes more
313  * fragmented in the course of time (i.e., the initial big holes break up into
314  * smaller holes), new table slots are needed to represent them.  These slots
315  * are taken from the list headed by 'free_slots'.
316  */
317   int i, first = 0;
318 
319   total_pages = 0;
320 
321   memset(free_pages_bitmap, 0, sizeof(free_pages_bitmap));
322 
323   /* Use the chunks of physical memory to allocate holes. */
324   for (i=NR_MEMS-1; i>=0; i--) {
325   	if (chunks[i].size > 0) {
326 		phys_bytes from = CLICK2ABS(chunks[i].base),
327 			to = CLICK2ABS(chunks[i].base+chunks[i].size)-1;
328 		if(first || from < mem_low) mem_low = from;
329 		if(first || to > mem_high) mem_high = to;
330 		free_mem(chunks[i].base, chunks[i].size);
331 		total_pages += chunks[i].size;
332 		first = 0;
333 	}
334   }
335 }
336 
337 #if SANITYCHECKS
338 void mem_sanitycheck(const char *file, int line)
339 {
340 	int i;
341 	for(i = 0; i < NUMBER_PHYSICAL_PAGES; i++) {
342 		if(!page_isfree(i)) continue;
343 		MYASSERT(usedpages_add(i * VM_PAGE_SIZE, VM_PAGE_SIZE) == OK);
344 	}
345 }
346 #endif
347 
348 void memstats(int *nodes, int *pages, int *largest)
349 {
350 	int i;
351 	*nodes = 0;
352 	*pages = 0;
353 	*largest = 0;
354 
355 	for(i = 0; i < NUMBER_PHYSICAL_PAGES; i++) {
356 		int size = 0;
357 		while(i < NUMBER_PHYSICAL_PAGES && page_isfree(i)) {
358 			size++;
359 			i++;
360 		}
361 		if(size == 0) continue;
362 		(*nodes)++;
363 		(*pages)+= size;
364 		if(size > *largest)
365 			*largest = size;
366 	}
367 }
368 
369 static int findbit(int low, int startscan, int pages, int memflags, int *len)
370 {
371 	int run_length = 0, i;
372 	int freerange_start = startscan;
373 
374 	for(i = startscan; i >= low; i--) {
375 		if(!page_isfree(i)) {
376 			int pi;
377 			int chunk = i/BITCHUNK_BITS, moved = 0;
378 			run_length = 0;
379 			pi = i;
380 			while(chunk > 0 &&
381 			   !MAP_CHUNK(free_pages_bitmap, chunk*BITCHUNK_BITS)) {
382 				chunk--;
383 				moved = 1;
384 			}
385 			if(moved) { i = chunk * BITCHUNK_BITS + BITCHUNK_BITS; }
386 			continue;
387 		}
388 		if(!run_length) { freerange_start = i; run_length = 1; }
389 		else { freerange_start--; run_length++; }
390 		assert(run_length <= pages);
391 		if(run_length == pages) {
392 			/* good block found! */
393 			*len = run_length;
394 			return freerange_start;
395 		}
396 	}
397 
398 	return NO_MEM;
399 }
400 
401 /*===========================================================================*
402  *				alloc_pages				     *
403  *===========================================================================*/
404 static phys_bytes alloc_pages(int pages, int memflags)
405 {
406 	phys_bytes boundary16 = 16 * 1024 * 1024 / VM_PAGE_SIZE;
407 	phys_bytes boundary1  =  1 * 1024 * 1024 / VM_PAGE_SIZE;
408 	phys_bytes mem = NO_MEM, i;	/* page number */
409 	int maxpage = NUMBER_PHYSICAL_PAGES - 1;
410 	static int lastscan = -1;
411 	int startscan, run_length;
412 
413 	if(memflags & PAF_LOWER16MB)
414 		maxpage = boundary16 - 1;
415 	else if(memflags & PAF_LOWER1MB)
416 		maxpage = boundary1 - 1;
417 	else {
418 		/* no position restrictions: check page cache */
419 		if(pages == 1) {
420 			while(free_page_cache_size > 0) {
421 				i = free_page_cache[free_page_cache_size-1];
422 				if(page_isfree(i)) {
423 					free_page_cache_size--;
424 					mem = i;
425 					assert(mem != NO_MEM);
426 					run_length = 1;
427 					break;
428 				}
429 				free_page_cache_size--;
430 			}
431 		}
432 	}
433 
434 	if(lastscan < maxpage && lastscan >= 0)
435 		startscan = lastscan;
436 	else	startscan = maxpage;
437 
438 	if(mem == NO_MEM)
439 		mem = findbit(0, startscan, pages, memflags, &run_length);
440 	if(mem == NO_MEM)
441 		mem = findbit(0, maxpage, pages, memflags, &run_length);
442 	if(mem == NO_MEM)
443 		return NO_MEM;
444 
445 	/* remember for next time */
446 	lastscan = mem;
447 
448 	for(i = mem; i < mem + pages; i++) {
449 		UNSET_BIT(free_pages_bitmap, i);
450 	}
451 
452 	if(memflags & PAF_CLEAR) {
453 		int s;
454 		if ((s= sys_memset(NONE, 0, CLICK_SIZE*mem,
455 			VM_PAGE_SIZE*pages)) != OK)
456 			panic("alloc_mem: sys_memset failed: %d", s);
457 	}
458 
459 	return mem;
460 }
461 
462 /*===========================================================================*
463  *				free_pages				     *
464  *===========================================================================*/
465 static void free_pages(phys_bytes pageno, int npages)
466 {
467 	int i, lim = pageno + npages - 1;
468 
469 #if JUNKFREE
470        if(sys_memset(NONE, 0xa5a5a5a5, VM_PAGE_SIZE * pageno,
471                VM_PAGE_SIZE * npages) != OK)
472                        panic("free_pages: sys_memset failed");
473 #endif
474 
475 	for(i = pageno; i <= lim; i++) {
476 		SET_BIT(free_pages_bitmap, i);
477 		if(free_page_cache_size < PAGE_CACHE_MAX) {
478 			free_page_cache[free_page_cache_size++] = i;
479 		}
480 	}
481 }
482 
483 /*===========================================================================*
484  *				printmemstats				     *
485  *===========================================================================*/
486 void printmemstats(void)
487 {
488 	int nodes, pages, largest;
489         memstats(&nodes, &pages, &largest);
490         printf("%d blocks, %d pages (%lukB) free, largest %d pages (%lukB)\n",
491                 nodes, pages, (unsigned long) pages * (VM_PAGE_SIZE/1024),
492 		largest, (unsigned long) largest * (VM_PAGE_SIZE/1024));
493 }
494 
495 
496 #if SANITYCHECKS
497 
498 /*===========================================================================*
499  *				usedpages_reset				     *
500  *===========================================================================*/
501 void usedpages_reset(void)
502 {
503 	memset(pagemap, 0, sizeof(pagemap));
504 }
505 
506 /*===========================================================================*
507  *				usedpages_add				     *
508  *===========================================================================*/
509 int usedpages_add_f(phys_bytes addr, phys_bytes len, const char *file, int line)
510 {
511 	u32_t pagestart, pages;
512 
513 	if(!incheck)
514 		return OK;
515 
516 	assert(!(addr % VM_PAGE_SIZE));
517 	assert(!(len % VM_PAGE_SIZE));
518 	assert(len > 0);
519 
520 	pagestart = addr / VM_PAGE_SIZE;
521 	pages = len / VM_PAGE_SIZE;
522 
523 	while(pages > 0) {
524 		phys_bytes thisaddr;
525 		assert(pagestart > 0);
526 		assert(pagestart < NUMBER_PHYSICAL_PAGES);
527 		thisaddr = pagestart * VM_PAGE_SIZE;
528 		assert(pagestart < NUMBER_PHYSICAL_PAGES);
529 		if(pagemap[pagestart].used) {
530 			static int warnings = 0;
531 			if(warnings++ < 100)
532 				printf("%s:%d: usedpages_add: addr 0x%lx reused, first %s:%d\n",
533 					file, line, thisaddr, pagemap[pagestart].file, pagemap[pagestart].line);
534 			util_stacktrace();
535 			return EFAULT;
536 		}
537 		pagemap[pagestart].used = 1;
538 		pagemap[pagestart].file = file;
539 		pagemap[pagestart].line = line;
540 		pages--;
541 		pagestart++;
542 	}
543 
544 	return OK;
545 }
546 
547 #endif
548 
549