source: mainline/kernel/generic/src/mm/frame.c@ 5df1963

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5df1963 was 5df1963, checked in by Martin Decky <martin@…>, 12 years ago

bitmap frame allocator does not keep track of the size of the allocated frame blocks
to avoid memory leaks the number of allocated frames needs to be passed explicitly during deallocation

  • Property mode set to 100644
File size: 31.3 KB
Line 
1/*
2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
4 * Copyright (c) 2009 Martin Decky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup genericmm
32 * @{
33 */
34
35/**
36 * @file
37 * @brief Physical frame allocator.
38 *
39 * This file contains the physical frame allocator and memory zone management.
40 * The frame allocator is built on top of the two-level bitmap structure.
41 *
42 */
43
44#include <typedefs.h>
45#include <mm/frame.h>
46#include <mm/reserve.h>
47#include <mm/as.h>
48#include <panic.h>
49#include <debug.h>
50#include <adt/list.h>
51#include <synch/mutex.h>
52#include <synch/condvar.h>
53#include <arch/asm.h>
54#include <arch.h>
55#include <print.h>
56#include <align.h>
57#include <mm/slab.h>
58#include <bitops.h>
59#include <macros.h>
60#include <config.h>
61#include <str.h>
62
63#define BITMAP_BLOCK_SIZE 1024
64
65zones_t zones;
66
67/*
68 * Synchronization primitives used to sleep when there is no memory
69 * available.
70 */
71static mutex_t mem_avail_mtx;
72static condvar_t mem_avail_cv;
73static size_t mem_avail_req = 0; /**< Number of frames requested. */
74static size_t mem_avail_gen = 0; /**< Generation counter. */
75
76/********************/
77/* Helper functions */
78/********************/
79
80NO_TRACE static inline size_t frame_index(zone_t *zone, frame_t *frame)
81{
82 return (size_t) (frame - zone->frames);
83}
84
85NO_TRACE static inline size_t frame_index_abs(zone_t *zone, frame_t *frame)
86{
87 return (size_t) (frame - zone->frames) + zone->base;
88}
89
90NO_TRACE static inline bool frame_index_valid(zone_t *zone, size_t index)
91{
92 return (index < zone->count);
93}
94
95/** Initialize frame structure.
96 *
97 * @param frame Frame structure to be initialized.
98 *
99 */
100NO_TRACE static void frame_initialize(frame_t *frame)
101{
102 frame->refcount = 0;
103 frame->parent = NULL;
104}
105
106/*******************/
107/* Zones functions */
108/*******************/
109
110/** Insert-sort zone into zones list.
111 *
112 * Assume interrupts are disabled and zones lock is
113 * locked.
114 *
115 * @param base Base frame of the newly inserted zone.
116 * @param count Number of frames of the newly inserted zone.
117 *
118 * @return Zone number on success, -1 on error.
119 *
120 */
121NO_TRACE static size_t zones_insert_zone(pfn_t base, size_t count,
122 zone_flags_t flags)
123{
124 if (zones.count + 1 == ZONES_MAX) {
125 printf("Maximum zone count %u exceeded!\n", ZONES_MAX);
126 return (size_t) -1;
127 }
128
129 size_t i;
130 for (i = 0; i < zones.count; i++) {
131 /* Check for overlap */
132 if (overlaps(zones.info[i].base, zones.info[i].count,
133 base, count)) {
134
135 /*
136 * If the overlaping zones are of the same type
137 * and the new zone is completely within the previous
138 * one, then quietly ignore the new zone.
139 *
140 */
141
142 if ((zones.info[i].flags != flags) ||
143 (!iswithin(zones.info[i].base, zones.info[i].count,
144 base, count))) {
145 printf("Zone (%p, %p) overlaps "
146 "with previous zone (%p %p)!\n",
147 (void *) PFN2ADDR(base), (void *) PFN2ADDR(count),
148 (void *) PFN2ADDR(zones.info[i].base),
149 (void *) PFN2ADDR(zones.info[i].count));
150 }
151
152 return (size_t) -1;
153 }
154 if (base < zones.info[i].base)
155 break;
156 }
157
158 /* Move other zones up */
159 for (size_t j = zones.count; j > i; j--)
160 zones.info[j] = zones.info[j - 1];
161
162 zones.count++;
163
164 return i;
165}
166
167/** Get total available frames.
168 *
169 * Assume interrupts are disabled and zones lock is
170 * locked.
171 *
172 * @return Total number of available frames.
173 *
174 */
175NO_TRACE static size_t frame_total_free_get_internal(void)
176{
177 size_t total = 0;
178 size_t i;
179
180 for (i = 0; i < zones.count; i++)
181 total += zones.info[i].free_count;
182
183 return total;
184}
185
186NO_TRACE size_t frame_total_free_get(void)
187{
188 size_t total;
189
190 irq_spinlock_lock(&zones.lock, true);
191 total = frame_total_free_get_internal();
192 irq_spinlock_unlock(&zones.lock, true);
193
194 return total;
195}
196
197
198/** Find a zone with a given frames.
199 *
200 * Assume interrupts are disabled and zones lock is
201 * locked.
202 *
203 * @param frame Frame number contained in zone.
204 * @param count Number of frames to look for.
205 * @param hint Used as zone hint.
206 *
207 * @return Zone index or -1 if not found.
208 *
209 */
210NO_TRACE size_t find_zone(pfn_t frame, size_t count, size_t hint)
211{
212 if (hint >= zones.count)
213 hint = 0;
214
215 size_t i = hint;
216 do {
217 if ((zones.info[i].base <= frame)
218 && (zones.info[i].base + zones.info[i].count >= frame + count))
219 return i;
220
221 i++;
222 if (i >= zones.count)
223 i = 0;
224
225 } while (i != hint);
226
227 return (size_t) -1;
228}
229
230/** @return True if zone can allocate specified number of frames */
231NO_TRACE static bool zone_can_alloc(zone_t *zone, size_t count,
232 pfn_t constraint)
233{
234 /*
235 * The function bitmap_allocate_range() does not modify
236 * the bitmap if the last argument is NULL.
237 */
238 return ((zone->flags & ZONE_AVAILABLE) &&
239 bitmap_allocate_range(&zone->bitmap, count, zone->base,
240 constraint, NULL));
241}
242
243/** Find a zone that can allocate specified number of frames
244 *
245 * Assume interrupts are disabled and zones lock is
246 * locked.
247 *
248 * @param count Number of free frames we are trying to find.
249 * @param flags Required flags of the target zone.
250 * @param constraint Indication of bits that cannot be set in the
251 * physical frame number of the first allocated frame.
252 * @param hind Preferred zone.
253 *
254 */
255NO_TRACE static size_t find_free_zone(size_t count, zone_flags_t flags,
256 pfn_t constraint, size_t hint)
257{
258 if (hint >= zones.count)
259 hint = 0;
260
261 size_t i = hint;
262 do {
263 /*
264 * Check whether the zone meets the search criteria.
265 */
266 if (ZONE_FLAGS_MATCH(zones.info[i].flags, flags)) {
267 /*
268 * Check if the zone can satisfy the allocation request.
269 */
270 if (zone_can_alloc(&zones.info[i], count, constraint))
271 return i;
272 }
273
274 i++;
275 if (i >= zones.count)
276 i = 0;
277
278 } while (i != hint);
279
280 return (size_t) -1;
281}
282
283/******************/
284/* Zone functions */
285/******************/
286
287/** Return frame from zone. */
288NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t index)
289{
290 ASSERT(index < zone->count);
291
292 return &zone->frames[index];
293}
294
295/** Allocate frame in particular zone.
296 *
297 * Assume zone is locked and is available for allocation.
298 * Panics if allocation is impossible.
299 *
300 * @param zone Zone to allocate from.
301 * @param count Number of frames to allocate
302 * @param constraint Indication of bits that cannot be set in the
303 * physical frame number of the first allocated frame.
304 *
305 * @return Frame index in zone.
306 *
307 */
308NO_TRACE static size_t zone_frame_alloc(zone_t *zone, size_t count,
309 pfn_t constraint)
310{
311 ASSERT(zone->flags & ZONE_AVAILABLE);
312
313 /* Allocate frames from zone */
314 size_t index;
315 int avail = bitmap_allocate_range(&zone->bitmap, count, zone->base,
316 constraint, &index);
317
318 ASSERT(avail);
319
320 /* Update frame reference count */
321 for (size_t i = 0; i < count; i++) {
322 frame_t *frame = zone_get_frame(zone, index + i);
323
324 ASSERT(frame->refcount == 0);
325 frame->refcount = 1;
326 }
327
328 /* Update zone information. */
329 zone->free_count -= count;
330 zone->busy_count += count;
331
332 return index;
333}
334
335/** Free frame from zone.
336 *
337 * Assume zone is locked and is available for deallocation.
338 *
339 * @param zone Pointer to zone from which the frame is to be freed.
340 * @param index Frame index relative to zone.
341 *
342 * @return Number of freed frames.
343 *
344 */
345NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t index)
346{
347 ASSERT(zone->flags & ZONE_AVAILABLE);
348
349 frame_t *frame = zone_get_frame(zone, index);
350
351 ASSERT(frame->refcount > 0);
352
353 if (!--frame->refcount) {
354 bitmap_free_range(&zone->bitmap, index, 1);
355
356 /* Update zone information. */
357 zone->free_count++;
358 zone->busy_count--;
359
360 return 1;
361 }
362
363 return 0;
364}
365
366/** Mark frame in zone unavailable to allocation. */
367NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t index)
368{
369 if (!(zone->flags & ZONE_AVAILABLE))
370 return;
371
372 frame_t *frame = zone_get_frame(zone, index);
373 if (frame->refcount > 0)
374 return;
375
376 bitmap_set_range(&zone->bitmap, index, 1);
377
378 zone->free_count--;
379 reserve_force_alloc(1);
380}
381
382/** Merge two zones.
383 *
384 * Assume z1 & z2 are locked and compatible and zones lock is
385 * locked.
386 *
387 * @param z1 First zone to merge.
388 * @param z2 Second zone to merge.
389 * @param old_z1 Original data of the first zone.
390 * @param confdata Merged zone configuration data.
391 *
392 */
393NO_TRACE static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1,
394 void *confdata)
395{
396 ASSERT(zones.info[z1].flags & ZONE_AVAILABLE);
397 ASSERT(zones.info[z2].flags & ZONE_AVAILABLE);
398 ASSERT(zones.info[z1].flags == zones.info[z2].flags);
399 ASSERT(zones.info[z1].base < zones.info[z2].base);
400 ASSERT(!overlaps(zones.info[z1].base, zones.info[z1].count,
401 zones.info[z2].base, zones.info[z2].count));
402
403 /* Difference between zone bases */
404 pfn_t base_diff = zones.info[z2].base - zones.info[z1].base;
405
406 zones.info[z1].count = base_diff + zones.info[z2].count;
407 zones.info[z1].free_count += zones.info[z2].free_count;
408 zones.info[z1].busy_count += zones.info[z2].busy_count;
409
410 bitmap_initialize(&zones.info[z1].bitmap, zones.info[z1].count,
411 BITMAP_BLOCK_SIZE, confdata +
412 (sizeof(frame_t) * zones.info[z1].count));
413
414 zones.info[z1].frames = (frame_t *) confdata;
415
416 /*
417 * Copy frames and bits from both zones to preserve parents, etc.
418 */
419
420 for (size_t i = 0; i < old_z1->count; i++) {
421 bitmap_set(&zones.info[z1].bitmap, i,
422 bitmap_get(&old_z1->bitmap, i));
423 zones.info[z1].frames[i] = old_z1->frames[i];
424 }
425
426 for (size_t i = 0; i < zones.info[z2].count; i++) {
427 bitmap_set(&zones.info[z1].bitmap, base_diff + i,
428 bitmap_get(&zones.info[z2].bitmap, i));
429 zones.info[z1].frames[base_diff + i] =
430 zones.info[z2].frames[i];
431 }
432}
433
434/** Return old configuration frames into the zone.
435 *
436 * We have two cases:
437 * - The configuration data is outside the zone
438 * -> do nothing (perhaps call frame_free?)
439 * - The configuration data was created by zone_create
440 * or updated by reduce_region -> free every frame
441 *
442 * @param znum The actual zone where freeing should occur.
443 * @param pfn Old zone configuration frame.
444 * @param count Old zone frame count.
445 *
446 */
447NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
448{
449 ASSERT(zones.info[znum].flags & ZONE_AVAILABLE);
450
451 size_t cframes = SIZE2FRAMES(zone_conf_size(count));
452
453 if ((pfn < zones.info[znum].base) ||
454 (pfn >= zones.info[znum].base + zones.info[znum].count))
455 return;
456
457 for (size_t i = 0; i < cframes; i++)
458 (void) zone_frame_free(&zones.info[znum],
459 pfn - zones.info[znum].base + i);
460}
461
462/** Merge zones z1 and z2.
463 *
464 * The merged zones must be 2 zones with no zone existing in between
465 * (which means that z2 = z1 + 1). Both zones must be available zones
466 * with the same flags.
467 *
468 * When you create a new zone, the frame allocator configuration does
469 * not to be 2^order size. Once the allocator is running it is no longer
470 * possible, merged configuration data occupies more space :-/
471 *
472 */
473bool zone_merge(size_t z1, size_t z2)
474{
475 irq_spinlock_lock(&zones.lock, true);
476
477 bool ret = true;
478
479 /*
480 * We can join only 2 zones with none existing inbetween,
481 * the zones have to be available and with the same
482 * set of flags
483 */
484 if ((z1 >= zones.count) || (z2 >= zones.count) || (z2 - z1 != 1) ||
485 (zones.info[z1].flags != zones.info[z2].flags)) {
486 ret = false;
487 goto errout;
488 }
489
490 pfn_t cframes = SIZE2FRAMES(zone_conf_size(
491 zones.info[z2].base - zones.info[z1].base
492 + zones.info[z2].count));
493
494 /* Allocate merged zone data inside one of the zones */
495 pfn_t pfn;
496 if (zone_can_alloc(&zones.info[z1], cframes, 0)) {
497 pfn = zones.info[z1].base +
498 zone_frame_alloc(&zones.info[z1], cframes, 0);
499 } else if (zone_can_alloc(&zones.info[z2], cframes, 0)) {
500 pfn = zones.info[z2].base +
501 zone_frame_alloc(&zones.info[z2], cframes, 0);
502 } else {
503 ret = false;
504 goto errout;
505 }
506
507 /* Preserve original data from z1 */
508 zone_t old_z1 = zones.info[z1];
509
510 /* Do zone merging */
511 zone_merge_internal(z1, z2, &old_z1, (void *) PA2KA(PFN2ADDR(pfn)));
512
513 /* Subtract zone information from busy frames */
514 zones.info[z1].busy_count -= cframes;
515
516 /* Free old zone information */
517 return_config_frames(z1,
518 ADDR2PFN(KA2PA((uintptr_t) old_z1.frames)), old_z1.count);
519 return_config_frames(z1,
520 ADDR2PFN(KA2PA((uintptr_t) zones.info[z2].frames)),
521 zones.info[z2].count);
522
523 /* Move zones down */
524 for (size_t i = z2 + 1; i < zones.count; i++)
525 zones.info[i - 1] = zones.info[i];
526
527 zones.count--;
528
529errout:
530 irq_spinlock_unlock(&zones.lock, true);
531
532 return ret;
533}
534
535/** Merge all mergeable zones into one big zone.
536 *
537 * It is reasonable to do this on systems where
538 * BIOS reports parts in chunks, so that we could
539 * have 1 zone (it's faster).
540 *
541 */
542void zone_merge_all(void)
543{
544 size_t i = 1;
545
546 while (i < zones.count) {
547 if (!zone_merge(i - 1, i))
548 i++;
549 }
550}
551
552/** Create new frame zone.
553 *
554 * @param zone Zone to construct.
555 * @param start Physical address of the first frame within the zone.
556 * @param count Count of frames in zone.
557 * @param flags Zone flags.
558 * @param confdata Configuration data of the zone.
559 *
560 * @return Initialized zone.
561 *
562 */
563NO_TRACE static void zone_construct(zone_t *zone, pfn_t start, size_t count,
564 zone_flags_t flags, void *confdata)
565{
566 zone->base = start;
567 zone->count = count;
568 zone->flags = flags;
569 zone->free_count = count;
570 zone->busy_count = 0;
571
572 if (flags & ZONE_AVAILABLE) {
573 /*
574 * Initialize frame bitmap (located after the array of
575 * frame_t structures in the configuration space).
576 */
577
578 bitmap_initialize(&zone->bitmap, count, BITMAP_BLOCK_SIZE,
579 confdata + (sizeof(frame_t) * count));
580
581 /*
582 * Initialize the array of frame_t structures.
583 */
584
585 zone->frames = (frame_t *) confdata;
586
587 for (size_t i = 0; i < count; i++)
588 frame_initialize(&zone->frames[i]);
589 } else {
590 bitmap_initialize(&zone->bitmap, 0, 0, NULL);
591 zone->frames = NULL;
592 }
593}
594
595/** Compute configuration data size for zone.
596 *
597 * @param count Size of zone in frames.
598 *
599 * @return Size of zone configuration info (in bytes).
600 *
601 */
602size_t zone_conf_size(size_t count)
603{
604 return (count * sizeof(frame_t) +
605 bitmap_size(count, BITMAP_BLOCK_SIZE));
606}
607
608/** Allocate external configuration frames from low memory. */
609pfn_t zone_external_conf_alloc(size_t count)
610{
611 size_t frames = SIZE2FRAMES(zone_conf_size(count));
612
613 return ADDR2PFN((uintptr_t)
614 frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
615}
616
617/** Create and add zone to system.
618 *
619 * @param start First frame number (absolute).
620 * @param count Size of zone in frames.
621 * @param confframe Where configuration frames are supposed to be.
622 * Automatically checks that we will not disturb the
623 * kernel and possibly init. If confframe is given
624 * _outside_ this zone, it is expected, that the area is
625 * already marked BUSY and big enough to contain
626 * zone_conf_size() amount of data. If the confframe is
627 * inside the area, the zone free frame information is
628 * modified not to include it.
629 *
630 * @return Zone number or -1 on error.
631 *
632 */
633size_t zone_create(pfn_t start, size_t count, pfn_t confframe,
634 zone_flags_t flags)
635{
636 irq_spinlock_lock(&zones.lock, true);
637
638 if (flags & ZONE_AVAILABLE) { /* Create available zone */
639 /*
640 * Theoretically we could have NULL here, practically make sure
641 * nobody tries to do that. If some platform requires, remove
642 * the assert
643 */
644 ASSERT(confframe != ADDR2PFN((uintptr_t ) NULL));
645
646 /* Update the known end of physical memory. */
647 config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
648
649 /*
650 * If confframe is supposed to be inside our zone, then make sure
651 * it does not span kernel & init
652 */
653 size_t confcount = SIZE2FRAMES(zone_conf_size(count));
654
655 if ((confframe >= start) && (confframe < start + count)) {
656 for (; confframe < start + count; confframe++) {
657 uintptr_t addr = PFN2ADDR(confframe);
658 if (overlaps(addr, PFN2ADDR(confcount),
659 KA2PA(config.base), config.kernel_size))
660 continue;
661
662 if (overlaps(addr, PFN2ADDR(confcount),
663 KA2PA(config.stack_base), config.stack_size))
664 continue;
665
666 bool overlap = false;
667 for (size_t i = 0; i < init.cnt; i++) {
668 if (overlaps(addr, PFN2ADDR(confcount),
669 init.tasks[i].paddr,
670 init.tasks[i].size)) {
671 overlap = true;
672 break;
673 }
674 }
675
676 if (overlap)
677 continue;
678
679 break;
680 }
681
682 if (confframe >= start + count)
683 panic("Cannot find configuration data for zone.");
684 }
685
686 size_t znum = zones_insert_zone(start, count, flags);
687 if (znum == (size_t) -1) {
688 irq_spinlock_unlock(&zones.lock, true);
689 return (size_t) -1;
690 }
691
692 void *confdata = (void *) PA2KA(PFN2ADDR(confframe));
693 zone_construct(&zones.info[znum], start, count, flags, confdata);
694
695 /* If confdata in zone, mark as unavailable */
696 if ((confframe >= start) && (confframe < start + count)) {
697 for (size_t i = confframe; i < confframe + confcount; i++)
698 zone_mark_unavailable(&zones.info[znum],
699 i - zones.info[znum].base);
700 }
701
702 irq_spinlock_unlock(&zones.lock, true);
703
704 return znum;
705 }
706
707 /* Non-available zone */
708 size_t znum = zones_insert_zone(start, count, flags);
709 if (znum == (size_t) -1) {
710 irq_spinlock_unlock(&zones.lock, true);
711 return (size_t) -1;
712 }
713
714 zone_construct(&zones.info[znum], start, count, flags, NULL);
715
716 irq_spinlock_unlock(&zones.lock, true);
717
718 return znum;
719}
720
721/*******************/
722/* Frame functions */
723/*******************/
724
725/** Set parent of frame. */
726void frame_set_parent(pfn_t pfn, void *data, size_t hint)
727{
728 irq_spinlock_lock(&zones.lock, true);
729
730 size_t znum = find_zone(pfn, 1, hint);
731
732 ASSERT(znum != (size_t) -1);
733
734 zone_get_frame(&zones.info[znum],
735 pfn - zones.info[znum].base)->parent = data;
736
737 irq_spinlock_unlock(&zones.lock, true);
738}
739
740void *frame_get_parent(pfn_t pfn, size_t hint)
741{
742 irq_spinlock_lock(&zones.lock, true);
743
744 size_t znum = find_zone(pfn, 1, hint);
745
746 ASSERT(znum != (size_t) -1);
747
748 void *res = zone_get_frame(&zones.info[znum],
749 pfn - zones.info[znum].base)->parent;
750
751 irq_spinlock_unlock(&zones.lock, true);
752
753 return res;
754}
755
756/** Allocate frames of physical memory.
757 *
758 * @param count Number of continuous frames to allocate.
759 * @param flags Flags for host zone selection and address processing.
760 * @param constraint Indication of physical address bits that cannot be
761 * set in the address of the first allocated frame.
762 * @param pzone Preferred zone.
763 *
764 * @return Physical address of the allocated frame.
765 *
766 */
767uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
768 uintptr_t constraint, size_t *pzone)
769{
770 ASSERT(count > 0);
771
772 size_t hint = pzone ? (*pzone) : 0;
773 pfn_t frame_constraint = ADDR2PFN(constraint);
774
775 /*
776 * If not told otherwise, we must first reserve the memory.
777 */
778 if (!(flags & FRAME_NO_RESERVE))
779 reserve_force_alloc(count);
780
781loop:
782 irq_spinlock_lock(&zones.lock, true);
783
784 /*
785 * First, find suitable frame zone.
786 */
787 size_t znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
788 frame_constraint, hint);
789
790 /*
791 * If no memory, reclaim some slab memory,
792 * if it does not help, reclaim all.
793 */
794 if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
795 irq_spinlock_unlock(&zones.lock, true);
796 size_t freed = slab_reclaim(0);
797 irq_spinlock_lock(&zones.lock, true);
798
799 if (freed > 0)
800 znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
801 frame_constraint, hint);
802
803 if (znum == (size_t) -1) {
804 irq_spinlock_unlock(&zones.lock, true);
805 freed = slab_reclaim(SLAB_RECLAIM_ALL);
806 irq_spinlock_lock(&zones.lock, true);
807
808 if (freed > 0)
809 znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
810 frame_constraint, hint);
811 }
812 }
813
814 if (znum == (size_t) -1) {
815 if (flags & FRAME_ATOMIC) {
816 irq_spinlock_unlock(&zones.lock, true);
817
818 if (!(flags & FRAME_NO_RESERVE))
819 reserve_free(count);
820
821 return 0;
822 }
823
824#ifdef CONFIG_DEBUG
825 size_t avail = frame_total_free_get_internal();
826#endif
827
828 irq_spinlock_unlock(&zones.lock, true);
829
830 if (!THREAD)
831 panic("Cannot wait for memory to become available.");
832
833 /*
834 * Sleep until some frames are available again.
835 */
836
837#ifdef CONFIG_DEBUG
838 printf("Thread %" PRIu64 " waiting for %zu frames, "
839 "%zu available.\n", THREAD->tid, count, avail);
840#endif
841
842 /*
843 * Since the mem_avail_mtx is an active mutex, we need to
844 * disable interrupts to prevent deadlock with TLB shootdown.
845 */
846 ipl_t ipl = interrupts_disable();
847 mutex_lock(&mem_avail_mtx);
848
849 if (mem_avail_req > 0)
850 mem_avail_req = min(mem_avail_req, count);
851 else
852 mem_avail_req = count;
853
854 size_t gen = mem_avail_gen;
855
856 while (gen == mem_avail_gen)
857 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
858
859 mutex_unlock(&mem_avail_mtx);
860 interrupts_restore(ipl);
861
862#ifdef CONFIG_DEBUG
863 printf("Thread %" PRIu64 " woken up.\n", THREAD->tid);
864#endif
865
866 goto loop;
867 }
868
869 pfn_t pfn = zone_frame_alloc(&zones.info[znum], count,
870 frame_constraint) + zones.info[znum].base;
871
872 irq_spinlock_unlock(&zones.lock, true);
873
874 if (pzone)
875 *pzone = znum;
876
877 return PFN2ADDR(pfn);
878}
879
880uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
881{
882 return frame_alloc_generic(count, flags, constraint, NULL);
883}
884
885uintptr_t frame_alloc_noreserve(size_t count, frame_flags_t flags,
886 uintptr_t constraint)
887{
888 return frame_alloc_generic(count, flags | FRAME_NO_RESERVE, constraint,
889 NULL);
890}
891
892/** Free frames of physical memory.
893 *
894 * Find respective frame structures for supplied physical frames.
895 * Decrement each frame reference count. If it drops to zero, mark
896 * the frames as available.
897 *
898 * @param start Physical Address of the first frame to be freed.
899 * @param count Number of frames to free.
900 * @param flags Flags to control memory reservation.
901 *
902 */
903void frame_free_generic(uintptr_t start, size_t count, frame_flags_t flags)
904{
905 size_t freed = 0;
906
907 irq_spinlock_lock(&zones.lock, true);
908
909 for (size_t i = 0; i < count; i++) {
910 /*
911 * First, find host frame zone for addr.
912 */
913 pfn_t pfn = ADDR2PFN(start) + i;
914 size_t znum = find_zone(pfn, 1, 0);
915
916 ASSERT(znum != (size_t) -1);
917
918 freed += zone_frame_free(&zones.info[znum],
919 pfn - zones.info[znum].base);
920 }
921
922 irq_spinlock_unlock(&zones.lock, true);
923
924 /*
925 * Signal that some memory has been freed.
926 * Since the mem_avail_mtx is an active mutex,
927 * we need to disable interruptsto prevent deadlock
928 * with TLB shootdown.
929 */
930
931 ipl_t ipl = interrupts_disable();
932 mutex_lock(&mem_avail_mtx);
933
934 if (mem_avail_req > 0)
935 mem_avail_req -= min(mem_avail_req, freed);
936
937 if (mem_avail_req == 0) {
938 mem_avail_gen++;
939 condvar_broadcast(&mem_avail_cv);
940 }
941
942 mutex_unlock(&mem_avail_mtx);
943 interrupts_restore(ipl);
944
945 if (!(flags & FRAME_NO_RESERVE))
946 reserve_free(freed);
947}
948
949void frame_free(uintptr_t frame, size_t count)
950{
951 frame_free_generic(frame, count, 0);
952}
953
954void frame_free_noreserve(uintptr_t frame, size_t count)
955{
956 frame_free_generic(frame, count, FRAME_NO_RESERVE);
957}
958
959/** Add reference to frame.
960 *
961 * Find respective frame structure for supplied PFN and
962 * increment frame reference count.
963 *
964 * @param pfn Frame number of the frame to be freed.
965 *
966 */
967NO_TRACE void frame_reference_add(pfn_t pfn)
968{
969 irq_spinlock_lock(&zones.lock, true);
970
971 /*
972 * First, find host frame zone for addr.
973 */
974 size_t znum = find_zone(pfn, 1, 0);
975
976 ASSERT(znum != (size_t) -1);
977
978 zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
979
980 irq_spinlock_unlock(&zones.lock, true);
981}
982
983/** Mark given range unavailable in frame zones.
984 *
985 */
986NO_TRACE void frame_mark_unavailable(pfn_t start, size_t count)
987{
988 irq_spinlock_lock(&zones.lock, true);
989
990 for (size_t i = 0; i < count; i++) {
991 size_t znum = find_zone(start + i, 1, 0);
992
993 if (znum == (size_t) -1) /* PFN not found */
994 continue;
995
996 zone_mark_unavailable(&zones.info[znum],
997 start + i - zones.info[znum].base);
998 }
999
1000 irq_spinlock_unlock(&zones.lock, true);
1001}
1002
1003/** Initialize physical memory management.
1004 *
1005 */
1006void frame_init(void)
1007{
1008 if (config.cpu_active == 1) {
1009 zones.count = 0;
1010 irq_spinlock_initialize(&zones.lock, "frame.zones.lock");
1011 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1012 condvar_initialize(&mem_avail_cv);
1013 }
1014
1015 /* Tell the architecture to create some memory */
1016 frame_low_arch_init();
1017
1018 if (config.cpu_active == 1) {
1019 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1020 SIZE2FRAMES(config.kernel_size));
1021 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
1022 SIZE2FRAMES(config.stack_size));
1023
1024 for (size_t i = 0; i < init.cnt; i++)
1025 frame_mark_unavailable(ADDR2PFN(init.tasks[i].paddr),
1026 SIZE2FRAMES(init.tasks[i].size));
1027
1028 if (ballocs.size)
1029 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1030 SIZE2FRAMES(ballocs.size));
1031
1032 /*
1033 * Blacklist first frame, as allocating NULL would
1034 * fail in some places
1035 */
1036 frame_mark_unavailable(0, 1);
1037 }
1038
1039 frame_high_arch_init();
1040}
1041
1042/** Adjust bounds of physical memory region according to low/high memory split.
1043 *
1044 * @param low[in] If true, the adjustment is performed to make the region
1045 * fit in the low memory. Otherwise the adjustment is
1046 * performed to make the region fit in the high memory.
1047 * @param basep[inout] Pointer to a variable which contains the region's base
1048 * address and which may receive the adjusted base address.
1049 * @param sizep[inout] Pointer to a variable which contains the region's size
1050 * and which may receive the adjusted size.
1051 *
1052 * @return True if the region still exists even after the adjustment.
1053 * @return False otherwise.
1054 *
1055 */
1056bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
1057{
1058 uintptr_t limit = KA2PA(config.identity_base) + config.identity_size;
1059
1060 if (low) {
1061 if (*basep > limit)
1062 return false;
1063
1064 if (*basep + *sizep > limit)
1065 *sizep = limit - *basep;
1066 } else {
1067 if (*basep + *sizep <= limit)
1068 return false;
1069
1070 if (*basep <= limit) {
1071 *sizep -= limit - *basep;
1072 *basep = limit;
1073 }
1074 }
1075
1076 return true;
1077}
1078
1079/** Return total size of all zones.
1080 *
1081 */
1082uint64_t zones_total_size(void)
1083{
1084 irq_spinlock_lock(&zones.lock, true);
1085
1086 uint64_t total = 0;
1087
1088 for (size_t i = 0; i < zones.count; i++)
1089 total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1090
1091 irq_spinlock_unlock(&zones.lock, true);
1092
1093 return total;
1094}
1095
1096void zones_stats(uint64_t *total, uint64_t *unavail, uint64_t *busy,
1097 uint64_t *free)
1098{
1099 ASSERT(total != NULL);
1100 ASSERT(unavail != NULL);
1101 ASSERT(busy != NULL);
1102 ASSERT(free != NULL);
1103
1104 irq_spinlock_lock(&zones.lock, true);
1105
1106 *total = 0;
1107 *unavail = 0;
1108 *busy = 0;
1109 *free = 0;
1110
1111 for (size_t i = 0; i < zones.count; i++) {
1112 *total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1113
1114 if (zones.info[i].flags & ZONE_AVAILABLE) {
1115 *busy += (uint64_t) FRAMES2SIZE(zones.info[i].busy_count);
1116 *free += (uint64_t) FRAMES2SIZE(zones.info[i].free_count);
1117 } else
1118 *unavail += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1119 }
1120
1121 irq_spinlock_unlock(&zones.lock, true);
1122}
1123
1124/** Prints list of zones.
1125 *
1126 */
1127void zones_print_list(void)
1128{
1129#ifdef __32_BITS__
1130 printf("[nr] [base addr] [frames ] [flags ] [free frames ] [busy frames ]\n");
1131#endif
1132
1133#ifdef __64_BITS__
1134 printf("[nr] [base address ] [frames ] [flags ] [free frames ] [busy frames ]\n");
1135#endif
1136
1137 /*
1138 * Because printing may require allocation of memory, we may not hold
1139 * the frame allocator locks when printing zone statistics. Therefore,
1140 * we simply gather the statistics under the protection of the locks and
1141 * print the statistics when the locks have been released.
1142 *
1143 * When someone adds/removes zones while we are printing the statistics,
1144 * we may end up with inaccurate output (e.g. a zone being skipped from
1145 * the listing).
1146 */
1147
1148 for (size_t i = 0;; i++) {
1149 irq_spinlock_lock(&zones.lock, true);
1150
1151 if (i >= zones.count) {
1152 irq_spinlock_unlock(&zones.lock, true);
1153 break;
1154 }
1155
1156 uintptr_t base = PFN2ADDR(zones.info[i].base);
1157 size_t count = zones.info[i].count;
1158 zone_flags_t flags = zones.info[i].flags;
1159 size_t free_count = zones.info[i].free_count;
1160 size_t busy_count = zones.info[i].busy_count;
1161
1162 irq_spinlock_unlock(&zones.lock, true);
1163
1164 bool available = ((flags & ZONE_AVAILABLE) != 0);
1165
1166 printf("%-4zu", i);
1167
1168#ifdef __32_BITS__
1169 printf(" %p", (void *) base);
1170#endif
1171
1172#ifdef __64_BITS__
1173 printf(" %p", (void *) base);
1174#endif
1175
1176 printf(" %12zu %c%c%c%c%c ", count,
1177 available ? 'A' : '-',
1178 (flags & ZONE_RESERVED) ? 'R' : '-',
1179 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1180 (flags & ZONE_LOWMEM) ? 'L' : '-',
1181 (flags & ZONE_HIGHMEM) ? 'H' : '-');
1182
1183 if (available)
1184 printf("%14zu %14zu",
1185 free_count, busy_count);
1186
1187 printf("\n");
1188 }
1189}
1190
1191/** Prints zone details.
1192 *
1193 * @param num Zone base address or zone number.
1194 *
1195 */
1196void zone_print_one(size_t num)
1197{
1198 irq_spinlock_lock(&zones.lock, true);
1199 size_t znum = (size_t) -1;
1200
1201 for (size_t i = 0; i < zones.count; i++) {
1202 if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
1203 znum = i;
1204 break;
1205 }
1206 }
1207
1208 if (znum == (size_t) -1) {
1209 irq_spinlock_unlock(&zones.lock, true);
1210 printf("Zone not found.\n");
1211 return;
1212 }
1213
1214 uintptr_t base = PFN2ADDR(zones.info[znum].base);
1215 zone_flags_t flags = zones.info[znum].flags;
1216 size_t count = zones.info[znum].count;
1217 size_t free_count = zones.info[znum].free_count;
1218 size_t busy_count = zones.info[znum].busy_count;
1219
1220 irq_spinlock_unlock(&zones.lock, true);
1221
1222 bool available = ((flags & ZONE_AVAILABLE) != 0);
1223
1224 uint64_t size;
1225 const char *size_suffix;
1226 bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
1227
1228 printf("Zone number: %zu\n", znum);
1229 printf("Zone base address: %p\n", (void *) base);
1230 printf("Zone size: %zu frames (%" PRIu64 " %s)\n", count,
1231 size, size_suffix);
1232 printf("Zone flags: %c%c%c%c%c\n",
1233 available ? 'A' : '-',
1234 (flags & ZONE_RESERVED) ? 'R' : '-',
1235 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1236 (flags & ZONE_LOWMEM) ? 'L' : '-',
1237 (flags & ZONE_HIGHMEM) ? 'H' : '-');
1238
1239 if (available) {
1240 bin_order_suffix(FRAMES2SIZE(busy_count), &size, &size_suffix,
1241 false);
1242 printf("Allocated space: %zu frames (%" PRIu64 " %s)\n",
1243 busy_count, size, size_suffix);
1244 bin_order_suffix(FRAMES2SIZE(free_count), &size, &size_suffix,
1245 false);
1246 printf("Available space: %zu frames (%" PRIu64 " %s)\n",
1247 free_count, size, size_suffix);
1248 }
1249}
1250
1251/** @}
1252 */
Note: See TracBrowser for help on using the repository browser.