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

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

Improve merging zones once more

It is not sufficient to just mark the frames in the gap between the
original zones as unavailable. The frames need to be properly
initialized as well.

  • Property mode set to 100644
File size: 37.0 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 kernel_generic_mm
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 <assert.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 <stdio.h>
56#include <log.h>
57#include <align.h>
58#include <mm/slab.h>
59#include <bitops.h>
60#include <macros.h>
61#include <config.h>
62#include <str.h>
63#include <proc/thread.h> /* THREAD */
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/** Initialize frame structure.
77 *
78 * @param frame Frame structure to be initialized.
79 *
80 */
81_NO_TRACE static void frame_initialize(frame_t *frame)
82{
83 frame->refcount = 0;
84 frame->parent = NULL;
85}
86
87/*
88 * Zones functions
89 */
90
91/** Insert-sort zone into zones list.
92 *
93 * Assume interrupts are disabled and zones lock is
94 * locked.
95 *
96 * @param base Base frame of the newly inserted zone.
97 * @param count Number of frames of the newly inserted zone.
98 *
99 * @return Zone number on success, -1 on error.
100 *
101 */
102_NO_TRACE static size_t zones_insert_zone(pfn_t base, size_t count,
103 zone_flags_t flags)
104{
105 if (zones.count + 1 == ZONES_MAX) {
106 log(LF_OTHER, LVL_ERROR, "Maximum zone count %u exceeded!",
107 ZONES_MAX);
108 return (size_t) -1;
109 }
110
111 size_t i;
112 for (i = 0; i < zones.count; i++) {
113 /* Check for overlap */
114 if (overlaps(zones.info[i].base, zones.info[i].count,
115 base, count)) {
116
117 /*
118 * If the overlaping zones are of the same type
119 * and the new zone is completely within the previous
120 * one, then quietly ignore the new zone.
121 *
122 */
123
124 if ((zones.info[i].flags != flags) ||
125 (!iswithin(zones.info[i].base, zones.info[i].count,
126 base, count))) {
127 log(LF_OTHER, LVL_WARN,
128 "Zone (%p, %p) overlaps "
129 "with previous zone (%p %p)!",
130 (void *) PFN2ADDR(base), (void *) PFN2ADDR(count),
131 (void *) PFN2ADDR(zones.info[i].base),
132 (void *) PFN2ADDR(zones.info[i].count));
133 }
134
135 return (size_t) -1;
136 }
137 if (base < zones.info[i].base)
138 break;
139 }
140
141 /* Move other zones up */
142 for (size_t j = zones.count; j > i; j--)
143 zones.info[j] = zones.info[j - 1];
144
145 zones.count++;
146
147 return i;
148}
149
150/** Get total available frames.
151 *
152 * Assume interrupts are disabled and zones lock is
153 * locked.
154 *
155 * @return Total number of available frames.
156 *
157 */
158_NO_TRACE static size_t frame_total_free_get_internal(void)
159{
160 size_t total = 0;
161 size_t i;
162
163 for (i = 0; i < zones.count; i++)
164 total += zones.info[i].free_count;
165
166 return total;
167}
168
169_NO_TRACE size_t frame_total_free_get(void)
170{
171 size_t total;
172
173 irq_spinlock_lock(&zones.lock, true);
174 total = frame_total_free_get_internal();
175 irq_spinlock_unlock(&zones.lock, true);
176
177 return total;
178}
179
180/** Find a zone with a given frames.
181 *
182 * Assume interrupts are disabled and zones lock is
183 * locked.
184 *
185 * @param frame Frame number contained in zone.
186 * @param count Number of frames to look for.
187 * @param hint Used as zone hint.
188 *
189 * @return Zone index or -1 if not found.
190 *
191 */
192_NO_TRACE size_t find_zone(pfn_t frame, size_t count, size_t hint)
193{
194 if (hint >= zones.count)
195 hint = 0;
196
197 size_t i = hint;
198 do {
199 if ((zones.info[i].base <= frame) &&
200 (zones.info[i].base + zones.info[i].count >= frame + count))
201 return i;
202
203 i++;
204 if (i >= zones.count)
205 i = 0;
206
207 } while (i != hint);
208
209 return (size_t) -1;
210}
211
212/** @return True if zone can allocate specified number of frames */
213_NO_TRACE static bool zone_can_alloc(zone_t *zone, size_t count,
214 pfn_t constraint)
215{
216 /*
217 * The function bitmap_allocate_range() does not modify
218 * the bitmap if the last argument is NULL.
219 */
220
221 return ((zone->flags & ZONE_AVAILABLE) &&
222 bitmap_allocate_range(&zone->bitmap, count, zone->base,
223 FRAME_LOWPRIO, constraint, NULL));
224}
225
226/** Find a zone that can allocate specified number of frames
227 *
228 * This function searches among all zones. Assume interrupts are
229 * disabled and zones lock is locked.
230 *
231 * @param count Number of free frames we are trying to find.
232 * @param flags Required flags of the zone.
233 * @param constraint Indication of bits that cannot be set in the
234 * physical frame number of the first allocated frame.
235 * @param hint Preferred zone.
236 *
237 * @return Zone that can allocate specified number of frames.
238 * @return -1 if no zone can satisfy the request.
239 *
240 */
241_NO_TRACE static size_t find_free_zone_all(size_t count, zone_flags_t flags,
242 pfn_t constraint, size_t hint)
243{
244 for (size_t pos = 0; pos < zones.count; pos++) {
245 size_t i = (pos + hint) % zones.count;
246
247 /* Check whether the zone meets the search criteria. */
248 if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
249 continue;
250
251 /* Check if the zone can satisfy the allocation request. */
252 if (zone_can_alloc(&zones.info[i], count, constraint))
253 return i;
254 }
255
256 return (size_t) -1;
257}
258
259/** Check if frame range priority memory
260 *
261 * @param pfn Starting frame.
262 * @param count Number of frames.
263 *
264 * @return True if the range contains only priority memory.
265 *
266 */
267_NO_TRACE static bool is_high_priority(pfn_t base, size_t count)
268{
269 return (base + count <= FRAME_LOWPRIO);
270}
271
272/** Find a zone that can allocate specified number of frames
273 *
274 * This function ignores zones that contain only high-priority
275 * memory. Assume interrupts are disabled and zones lock is locked.
276 *
277 * @param count Number of free frames we are trying to find.
278 * @param flags Required flags of the zone.
279 * @param constraint Indication of bits that cannot be set in the
280 * physical frame number of the first allocated frame.
281 * @param hint Preferred zone.
282 *
283 * @return Zone that can allocate specified number of frames.
284 * @return -1 if no low-priority zone can satisfy the request.
285 *
286 */
287_NO_TRACE static size_t find_free_zone_lowprio(size_t count, zone_flags_t flags,
288 pfn_t constraint, size_t hint)
289{
290 for (size_t pos = 0; pos < zones.count; pos++) {
291 size_t i = (pos + hint) % zones.count;
292
293 /* Skip zones containing only high-priority memory. */
294 if (is_high_priority(zones.info[i].base, zones.info[i].count))
295 continue;
296
297 /* Check whether the zone meets the search criteria. */
298 if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
299 continue;
300
301 /* Check if the zone can satisfy the allocation request. */
302 if (zone_can_alloc(&zones.info[i], count, constraint))
303 return i;
304 }
305
306 return (size_t) -1;
307}
308
309/** Find a zone that can allocate specified number of frames
310 *
311 * Assume interrupts are disabled and zones lock is
312 * locked.
313 *
314 * @param count Number of free frames we are trying to find.
315 * @param flags Required flags of the target zone.
316 * @param constraint Indication of bits that cannot be set in the
317 * physical frame number of the first allocated frame.
318 * @param hint Preferred zone.
319 *
320 * @return Zone that can allocate specified number of frames.
321 * @return -1 if no zone can satisfy the request.
322 *
323 */
324_NO_TRACE static size_t find_free_zone(size_t count, zone_flags_t flags,
325 pfn_t constraint, size_t hint)
326{
327 if (hint >= zones.count)
328 hint = 0;
329
330 /*
331 * Prefer zones with low-priority memory over
332 * zones with high-priority memory.
333 */
334
335 size_t znum = find_free_zone_lowprio(count, flags, constraint, hint);
336 if (znum != (size_t) -1)
337 return znum;
338
339 /* Take all zones into account */
340 return find_free_zone_all(count, flags, constraint, hint);
341}
342
343/*
344 * Zone functions
345 */
346
347/** Return frame from zone. */
348_NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t index)
349{
350 assert(index < zone->count);
351
352 return &zone->frames[index];
353}
354
355/** Allocate frame in particular zone.
356 *
357 * Assume zone is locked and is available for allocation.
358 * Panics if allocation is impossible.
359 *
360 * @param zone Zone to allocate from.
361 * @param count Number of frames to allocate
362 * @param constraint Indication of bits that cannot be set in the
363 * physical frame number of the first allocated frame.
364 *
365 * @return Frame index in zone.
366 *
367 */
368_NO_TRACE static size_t zone_frame_alloc(zone_t *zone, size_t count,
369 pfn_t constraint)
370{
371 assert(zone->flags & ZONE_AVAILABLE);
372 assert(zone->free_count >= count);
373
374 /* Allocate frames from zone */
375 size_t index = (size_t) -1;
376 int avail = bitmap_allocate_range(&zone->bitmap, count, zone->base,
377 FRAME_LOWPRIO, constraint, &index);
378
379 (void) avail;
380 assert(avail);
381 assert(index != (size_t) -1);
382
383 /* Update frame reference count */
384 for (size_t i = 0; i < count; i++) {
385 frame_t *frame = zone_get_frame(zone, index + i);
386
387 assert(frame->refcount == 0);
388 frame->refcount = 1;
389 }
390
391 /* Update zone information. */
392 zone->free_count -= count;
393 zone->busy_count += count;
394
395 return index;
396}
397
398/** Free frame from zone.
399 *
400 * Assume zone is locked and is available for deallocation.
401 *
402 * @param zone Pointer to zone from which the frame is to be freed.
403 * @param index Frame index relative to zone.
404 *
405 * @return Number of freed frames.
406 *
407 */
408_NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t index)
409{
410 assert(zone->flags & ZONE_AVAILABLE);
411
412 frame_t *frame = zone_get_frame(zone, index);
413 assert(frame->refcount > 0);
414
415 if (!--frame->refcount) {
416 assert(zone->busy_count > 0);
417
418 bitmap_set(&zone->bitmap, index, 0);
419
420 /* Update zone information. */
421 zone->free_count++;
422 zone->busy_count--;
423
424 return 1;
425 }
426
427 return 0;
428}
429
430/** Mark frame in zone unavailable to allocation. */
431_NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t index)
432{
433 assert(zone->flags & ZONE_AVAILABLE);
434
435 frame_t *frame = zone_get_frame(zone, index);
436 assert(frame->refcount <= 1);
437
438 if (frame->refcount > 0)
439 return;
440
441 assert(zone->free_count > 0);
442
443 frame->refcount = 1;
444 bitmap_set_range(&zone->bitmap, index, 1);
445
446 zone->free_count--;
447 reserve_force_alloc(1);
448}
449
450/** Mark frame in zone available to allocation. */
451_NO_TRACE static void zone_mark_available(zone_t *zone, size_t index)
452{
453 assert(zone->flags & ZONE_AVAILABLE);
454
455 frame_t *frame = zone_get_frame(zone, index);
456 assert(frame->refcount == 1);
457
458 frame->refcount = 0;
459 bitmap_set_range(&zone->bitmap, index, 0);
460
461 zone->free_count++;
462}
463
464/** Merge two zones.
465 *
466 * Assume z1 & z2 are locked and compatible and zones lock is
467 * locked.
468 *
469 * @param z1 First zone to merge.
470 * @param z2 Second zone to merge.
471 * @param old_z1 Original data of the first zone.
472 * @param confdata Merged zone configuration data.
473 *
474 */
475_NO_TRACE static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1,
476 void *confdata)
477{
478 assert(zones.info[z1].flags & ZONE_AVAILABLE);
479 assert(zones.info[z2].flags & ZONE_AVAILABLE);
480 assert(zones.info[z1].flags == zones.info[z2].flags);
481 assert(zones.info[z1].base < zones.info[z2].base);
482 assert(!overlaps(zones.info[z1].base, zones.info[z1].count,
483 zones.info[z2].base, zones.info[z2].count));
484
485 /* Difference between zone bases */
486 pfn_t base_diff = zones.info[z2].base - zones.info[z1].base;
487 pfn_t gap = base_diff - zones.info[z1].count;
488
489 zones.info[z1].count = base_diff + zones.info[z2].count;
490 zones.info[z1].free_count += zones.info[z2].free_count;
491 zones.info[z1].busy_count += zones.info[z2].busy_count;
492
493 bitmap_initialize(&zones.info[z1].bitmap, zones.info[z1].count,
494 confdata + (sizeof(frame_t) * zones.info[z1].count));
495 bitmap_clear_range(&zones.info[z1].bitmap, 0, zones.info[z1].count);
496
497 zones.info[z1].frames = (frame_t *) confdata;
498
499 /*
500 * Copy frames and bits from both zones to preserve parents, etc.
501 */
502
503 for (size_t i = 0; i < old_z1->count; i++) {
504 bitmap_set(&zones.info[z1].bitmap, i,
505 bitmap_get(&old_z1->bitmap, i));
506 zones.info[z1].frames[i] = old_z1->frames[i];
507 }
508
509 for (size_t i = 0; i < zones.info[z2].count; i++) {
510 bitmap_set(&zones.info[z1].bitmap, base_diff + i,
511 bitmap_get(&zones.info[z2].bitmap, i));
512 zones.info[z1].frames[base_diff + i] =
513 zones.info[z2].frames[i];
514 }
515
516 /*
517 * Mark the gap between the original zones as unavailable.
518 */
519
520 for (size_t i = 0; i < gap; i++) {
521 frame_initialize(&zones.info[z1].frames[old_z1->count + i]);
522 zone_mark_unavailable(&zones.info[z1], old_z1->count + i);
523 }
524}
525
526/** Return old configuration frames into the zone.
527 *
528 * We have two cases:
529 * - The configuration data is outside the zone
530 * -> do nothing (perhaps call frame_free?)
531 * - The configuration data was created by zone_create
532 * or updated by reduce_region -> free every frame
533 *
534 * @param znum The actual zone where freeing should occur.
535 * @param pfn Old zone configuration frame.
536 * @param count Old zone frame count.
537 *
538 */
539_NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
540{
541 assert(zones.info[znum].flags & ZONE_AVAILABLE);
542
543 size_t cframes = SIZE2FRAMES(zone_conf_size(count));
544
545 if ((pfn < zones.info[znum].base) ||
546 (pfn >= zones.info[znum].base + zones.info[znum].count))
547 return;
548
549 for (size_t i = 0; i < cframes; i++)
550 zone_mark_available(&zones.info[znum],
551 pfn - zones.info[znum].base + i);
552}
553
554/** Merge zones z1 and z2.
555 *
556 * The merged zones must be 2 zones with no zone existing in between
557 * (which means that z2 = z1 + 1). Both zones must be available zones
558 * with the same flags.
559 *
560 * When you create a new zone, the frame allocator configuration does
561 * not to be 2^order size. Once the allocator is running it is no longer
562 * possible, merged configuration data occupies more space :-/
563 *
564 */
565bool zone_merge(size_t z1, size_t z2)
566{
567 irq_spinlock_lock(&zones.lock, true);
568
569 bool ret = true;
570
571 /*
572 * We can join only 2 zones with none existing inbetween,
573 * the zones have to be available and with the same
574 * set of flags
575 */
576 if ((z1 >= zones.count) || (z2 >= zones.count) || (z2 - z1 != 1) ||
577 (zones.info[z1].flags != zones.info[z2].flags)) {
578 ret = false;
579 goto errout;
580 }
581
582 pfn_t cframes = SIZE2FRAMES(zone_conf_size(
583 zones.info[z2].base - zones.info[z1].base +
584 zones.info[z2].count));
585
586 /* Allocate merged zone data inside one of the zones */
587 pfn_t pfn;
588 if (zone_can_alloc(&zones.info[z1], cframes, 0)) {
589 pfn = zones.info[z1].base +
590 zone_frame_alloc(&zones.info[z1], cframes, 0);
591 } else if (zone_can_alloc(&zones.info[z2], cframes, 0)) {
592 pfn = zones.info[z2].base +
593 zone_frame_alloc(&zones.info[z2], cframes, 0);
594 } else {
595 ret = false;
596 goto errout;
597 }
598
599 /* Preserve original data from z1 */
600 zone_t old_z1 = zones.info[z1];
601
602 /* Do zone merging */
603 zone_merge_internal(z1, z2, &old_z1, (void *) PA2KA(PFN2ADDR(pfn)));
604
605 /* Subtract zone information from busy frames */
606 zones.info[z1].busy_count -= cframes;
607
608 /* Free old zone information */
609 return_config_frames(z1,
610 ADDR2PFN(KA2PA((uintptr_t) old_z1.frames)), old_z1.count);
611 return_config_frames(z1,
612 ADDR2PFN(KA2PA((uintptr_t) zones.info[z2].frames)),
613 zones.info[z2].count);
614
615 /* Move zones down */
616 for (size_t i = z2 + 1; i < zones.count; i++)
617 zones.info[i - 1] = zones.info[i];
618
619 zones.count--;
620
621errout:
622 irq_spinlock_unlock(&zones.lock, true);
623
624 return ret;
625}
626
627/** Merge all mergeable zones into one big zone.
628 *
629 * It is reasonable to do this on systems where
630 * BIOS reports parts in chunks, so that we could
631 * have 1 zone (it's faster).
632 *
633 */
634void zone_merge_all(void)
635{
636 size_t i = 1;
637
638 while (i < zones.count) {
639 if (!zone_merge(i - 1, i))
640 i++;
641 }
642}
643
644/** Create new frame zone.
645 *
646 * @param zone Zone to construct.
647 * @param start Physical address of the first frame within the zone.
648 * @param count Count of frames in zone.
649 * @param flags Zone flags.
650 * @param confdata Configuration data of the zone.
651 *
652 * @return Initialized zone.
653 *
654 */
655_NO_TRACE static void zone_construct(zone_t *zone, pfn_t start, size_t count,
656 zone_flags_t flags, void *confdata)
657{
658 zone->base = start;
659 zone->count = count;
660 zone->flags = flags;
661 zone->free_count = count;
662 zone->busy_count = 0;
663
664 if (flags & ZONE_AVAILABLE) {
665 /*
666 * Initialize frame bitmap (located after the array of
667 * frame_t structures in the configuration space).
668 */
669
670 bitmap_initialize(&zone->bitmap, count, confdata +
671 (sizeof(frame_t) * count));
672 bitmap_clear_range(&zone->bitmap, 0, count);
673
674 /*
675 * Initialize the array of frame_t structures.
676 */
677
678 zone->frames = (frame_t *) confdata;
679
680 for (size_t i = 0; i < count; i++)
681 frame_initialize(&zone->frames[i]);
682 } else {
683 bitmap_initialize(&zone->bitmap, 0, NULL);
684 zone->frames = NULL;
685 }
686}
687
688/** Compute configuration data size for zone.
689 *
690 * @param count Size of zone in frames.
691 *
692 * @return Size of zone configuration info (in bytes).
693 *
694 */
695size_t zone_conf_size(size_t count)
696{
697 return (count * sizeof(frame_t) + bitmap_size(count));
698}
699
700/** Allocate external configuration frames from low memory. */
701pfn_t zone_external_conf_alloc(size_t count)
702{
703 size_t frames = SIZE2FRAMES(zone_conf_size(count));
704
705 return ADDR2PFN((uintptr_t)
706 frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
707}
708
709/** Create and add zone to system.
710 *
711 * @param start First frame number (absolute).
712 * @param count Size of zone in frames.
713 * @param confframe Where configuration frames are supposed to be.
714 * Automatically checks that we will not disturb the
715 * kernel and possibly init. If confframe is given
716 * _outside_ this zone, it is expected, that the area is
717 * already marked BUSY and big enough to contain
718 * zone_conf_size() amount of data. If the confframe is
719 * inside the area, the zone free frame information is
720 * modified not to include it.
721 *
722 * @return Zone number or -1 on error.
723 *
724 */
725size_t zone_create(pfn_t start, size_t count, pfn_t confframe,
726 zone_flags_t flags)
727{
728 irq_spinlock_lock(&zones.lock, true);
729
730 if (flags & ZONE_AVAILABLE) { /* Create available zone */
731 /*
732 * Theoretically we could have NULL here, practically make sure
733 * nobody tries to do that. If some platform requires, remove
734 * the assert
735 */
736 assert(confframe != ADDR2PFN((uintptr_t) NULL));
737
738 /* Update the known end of physical memory. */
739 config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
740
741 /*
742 * If confframe is supposed to be inside our zone, then make sure
743 * it does not span kernel & init
744 */
745 size_t confcount = SIZE2FRAMES(zone_conf_size(count));
746
747 if ((confframe >= start) && (confframe < start + count)) {
748 for (; confframe < start + count; confframe++) {
749 uintptr_t addr = PFN2ADDR(confframe);
750 if (overlaps(addr, PFN2ADDR(confcount),
751 KA2PA(config.base), config.kernel_size))
752 continue;
753
754 if (overlaps(addr, PFN2ADDR(confcount),
755 KA2PA(config.stack_base), config.stack_size))
756 continue;
757
758 bool overlap = false;
759 for (size_t i = 0; i < init.cnt; i++) {
760 if (overlaps(addr, PFN2ADDR(confcount),
761 init.tasks[i].paddr,
762 init.tasks[i].size)) {
763 overlap = true;
764 break;
765 }
766 }
767
768 if (overlap)
769 continue;
770
771 break;
772 }
773
774 if (confframe >= start + count)
775 panic("Cannot find configuration data for zone.");
776 }
777
778 size_t znum = zones_insert_zone(start, count, flags);
779 if (znum == (size_t) -1) {
780 irq_spinlock_unlock(&zones.lock, true);
781 return (size_t) -1;
782 }
783
784 void *confdata = (void *) PA2KA(PFN2ADDR(confframe));
785 zone_construct(&zones.info[znum], start, count, flags, confdata);
786
787 /* If confdata in zone, mark as unavailable */
788 if ((confframe >= start) && (confframe < start + count)) {
789 for (size_t i = confframe; i < confframe + confcount; i++)
790 zone_mark_unavailable(&zones.info[znum],
791 i - zones.info[znum].base);
792 }
793
794 irq_spinlock_unlock(&zones.lock, true);
795
796 return znum;
797 }
798
799 /* Non-available zone */
800 size_t znum = zones_insert_zone(start, count, flags);
801 if (znum == (size_t) -1) {
802 irq_spinlock_unlock(&zones.lock, true);
803 return (size_t) -1;
804 }
805
806 zone_construct(&zones.info[znum], start, count, flags, NULL);
807
808 irq_spinlock_unlock(&zones.lock, true);
809
810 return znum;
811}
812
813/*
814 * Frame functions
815 */
816
817/** Set parent of frame. */
818void frame_set_parent(pfn_t pfn, void *data, size_t hint)
819{
820 irq_spinlock_lock(&zones.lock, true);
821
822 size_t znum = find_zone(pfn, 1, hint);
823
824 assert(znum != (size_t) -1);
825
826 zone_get_frame(&zones.info[znum],
827 pfn - zones.info[znum].base)->parent = data;
828
829 irq_spinlock_unlock(&zones.lock, true);
830}
831
832void *frame_get_parent(pfn_t pfn, size_t hint)
833{
834 irq_spinlock_lock(&zones.lock, true);
835
836 size_t znum = find_zone(pfn, 1, hint);
837
838 assert(znum != (size_t) -1);
839
840 void *res = zone_get_frame(&zones.info[znum],
841 pfn - zones.info[znum].base)->parent;
842
843 irq_spinlock_unlock(&zones.lock, true);
844
845 return res;
846}
847
848static size_t try_find_zone(size_t count, bool lowmem,
849 pfn_t frame_constraint, size_t hint)
850{
851 if (!lowmem) {
852 size_t znum = find_free_zone(count,
853 ZONE_HIGHMEM | ZONE_AVAILABLE, frame_constraint, hint);
854 if (znum != (size_t) -1)
855 return znum;
856 }
857
858 return find_free_zone(count, ZONE_LOWMEM | ZONE_AVAILABLE,
859 frame_constraint, hint);
860}
861
862/** Allocate frames of physical memory.
863 *
864 * @param count Number of continuous frames to allocate.
865 * @param flags Flags for host zone selection and address processing.
866 * @param constraint Indication of physical address bits that cannot be
867 * set in the address of the first allocated frame.
868 * @param pzone Preferred zone.
869 *
870 * @return Physical address of the allocated frame.
871 *
872 */
873uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
874 uintptr_t constraint, size_t *pzone)
875{
876 assert(count > 0);
877
878 size_t hint = pzone ? (*pzone) : 0;
879 pfn_t frame_constraint = ADDR2PFN(constraint);
880
881 /*
882 * If not told otherwise, we must first reserve the memory.
883 */
884 if (!(flags & FRAME_NO_RESERVE))
885 reserve_force_alloc(count);
886
887loop:
888 irq_spinlock_lock(&zones.lock, true);
889
890 // TODO: Print diagnostic if neither is explicitly specified.
891 bool lowmem = (flags & FRAME_LOWMEM) || !(flags & FRAME_HIGHMEM);
892
893 /*
894 * First, find suitable frame zone.
895 */
896 size_t znum = try_find_zone(count, lowmem, frame_constraint, hint);
897
898 /*
899 * If no memory, reclaim some slab memory,
900 * if it does not help, reclaim all.
901 */
902 if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
903 irq_spinlock_unlock(&zones.lock, true);
904 size_t freed = slab_reclaim(0);
905 irq_spinlock_lock(&zones.lock, true);
906
907 if (freed > 0)
908 znum = try_find_zone(count, lowmem,
909 frame_constraint, hint);
910
911 if (znum == (size_t) -1) {
912 irq_spinlock_unlock(&zones.lock, true);
913 freed = slab_reclaim(SLAB_RECLAIM_ALL);
914 irq_spinlock_lock(&zones.lock, true);
915
916 if (freed > 0)
917 znum = try_find_zone(count, lowmem,
918 frame_constraint, hint);
919 }
920 }
921
922 if (znum == (size_t) -1) {
923 if (flags & FRAME_ATOMIC) {
924 irq_spinlock_unlock(&zones.lock, true);
925
926 if (!(flags & FRAME_NO_RESERVE))
927 reserve_free(count);
928
929 return 0;
930 }
931
932 size_t avail = frame_total_free_get_internal();
933
934 irq_spinlock_unlock(&zones.lock, true);
935
936 if (!THREAD)
937 panic("Cannot wait for %zu frames to become available "
938 "(%zu available).", count, avail);
939
940 /*
941 * Sleep until some frames are available again.
942 */
943
944#ifdef CONFIG_DEBUG
945 log(LF_OTHER, LVL_DEBUG,
946 "Thread %" PRIu64 " waiting for %zu frames "
947 "%zu available.", THREAD->tid, count, avail);
948#endif
949
950 /*
951 * Since the mem_avail_mtx is an active mutex, we need to
952 * disable interrupts to prevent deadlock with TLB shootdown.
953 */
954 ipl_t ipl = interrupts_disable();
955 mutex_lock(&mem_avail_mtx);
956
957 if (mem_avail_req > 0)
958 mem_avail_req = min(mem_avail_req, count);
959 else
960 mem_avail_req = count;
961
962 size_t gen = mem_avail_gen;
963
964 while (gen == mem_avail_gen)
965 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
966
967 mutex_unlock(&mem_avail_mtx);
968 interrupts_restore(ipl);
969
970#ifdef CONFIG_DEBUG
971 log(LF_OTHER, LVL_DEBUG, "Thread %" PRIu64 " woken up.",
972 THREAD->tid);
973#endif
974
975 goto loop;
976 }
977
978 pfn_t pfn = zone_frame_alloc(&zones.info[znum], count,
979 frame_constraint) + zones.info[znum].base;
980
981 irq_spinlock_unlock(&zones.lock, true);
982
983 if (pzone)
984 *pzone = znum;
985
986 return PFN2ADDR(pfn);
987}
988
989uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
990{
991 return frame_alloc_generic(count, flags, constraint, NULL);
992}
993
994/** Free frames of physical memory.
995 *
996 * Find respective frame structures for supplied physical frames.
997 * Decrement each frame reference count. If it drops to zero, mark
998 * the frames as available.
999 *
1000 * @param start Physical Address of the first frame to be freed.
1001 * @param count Number of frames to free.
1002 * @param flags Flags to control memory reservation.
1003 *
1004 */
1005void frame_free_generic(uintptr_t start, size_t count, frame_flags_t flags)
1006{
1007 size_t freed = 0;
1008
1009 irq_spinlock_lock(&zones.lock, true);
1010
1011 for (size_t i = 0; i < count; i++) {
1012 /*
1013 * First, find host frame zone for addr.
1014 */
1015 pfn_t pfn = ADDR2PFN(start) + i;
1016 size_t znum = find_zone(pfn, 1, 0);
1017
1018 assert(znum != (size_t) -1);
1019
1020 freed += zone_frame_free(&zones.info[znum],
1021 pfn - zones.info[znum].base);
1022 }
1023
1024 irq_spinlock_unlock(&zones.lock, true);
1025
1026 /*
1027 * Signal that some memory has been freed.
1028 * Since the mem_avail_mtx is an active mutex,
1029 * we need to disable interruptsto prevent deadlock
1030 * with TLB shootdown.
1031 */
1032
1033 ipl_t ipl = interrupts_disable();
1034 mutex_lock(&mem_avail_mtx);
1035
1036 if (mem_avail_req > 0)
1037 mem_avail_req -= min(mem_avail_req, freed);
1038
1039 if (mem_avail_req == 0) {
1040 mem_avail_gen++;
1041 condvar_broadcast(&mem_avail_cv);
1042 }
1043
1044 mutex_unlock(&mem_avail_mtx);
1045 interrupts_restore(ipl);
1046
1047 if (!(flags & FRAME_NO_RESERVE))
1048 reserve_free(freed);
1049}
1050
1051void frame_free(uintptr_t frame, size_t count)
1052{
1053 frame_free_generic(frame, count, 0);
1054}
1055
1056void frame_free_noreserve(uintptr_t frame, size_t count)
1057{
1058 frame_free_generic(frame, count, FRAME_NO_RESERVE);
1059}
1060
1061/** Add reference to frame.
1062 *
1063 * Find respective frame structure for supplied PFN and
1064 * increment frame reference count.
1065 *
1066 * @param pfn Frame number of the frame to be freed.
1067 *
1068 */
1069_NO_TRACE void frame_reference_add(pfn_t pfn)
1070{
1071 irq_spinlock_lock(&zones.lock, true);
1072
1073 /*
1074 * First, find host frame zone for addr.
1075 */
1076 size_t znum = find_zone(pfn, 1, 0);
1077
1078 assert(znum != (size_t) -1);
1079
1080 zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
1081
1082 irq_spinlock_unlock(&zones.lock, true);
1083}
1084
1085/** Mark given range unavailable in frame zones.
1086 *
1087 */
1088_NO_TRACE void frame_mark_unavailable(pfn_t start, size_t count)
1089{
1090 irq_spinlock_lock(&zones.lock, true);
1091
1092 for (size_t i = 0; i < count; i++) {
1093 size_t znum = find_zone(start + i, 1, 0);
1094
1095 if (znum == (size_t) -1) /* PFN not found */
1096 continue;
1097
1098 zone_mark_unavailable(&zones.info[znum],
1099 start + i - zones.info[znum].base);
1100 }
1101
1102 irq_spinlock_unlock(&zones.lock, true);
1103}
1104
1105/** Initialize physical memory management.
1106 *
1107 */
1108void frame_init(void)
1109{
1110 if (config.cpu_active == 1) {
1111 zones.count = 0;
1112 irq_spinlock_initialize(&zones.lock, "frame.zones.lock");
1113 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1114 condvar_initialize(&mem_avail_cv);
1115 }
1116
1117 /* Tell the architecture to create some memory */
1118 frame_low_arch_init();
1119
1120 if (config.cpu_active == 1) {
1121 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1122 SIZE2FRAMES(config.kernel_size));
1123 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
1124 SIZE2FRAMES(config.stack_size));
1125
1126 for (size_t i = 0; i < init.cnt; i++)
1127 frame_mark_unavailable(ADDR2PFN(init.tasks[i].paddr),
1128 SIZE2FRAMES(init.tasks[i].size));
1129
1130 if (ballocs.size)
1131 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1132 SIZE2FRAMES(ballocs.size));
1133
1134 /*
1135 * Blacklist first frame, as allocating NULL would
1136 * fail in some places
1137 */
1138 frame_mark_unavailable(0, 1);
1139 }
1140
1141 frame_high_arch_init();
1142}
1143
1144/** Adjust bounds of physical memory region according to low/high memory split.
1145 *
1146 * @param low[in] If true, the adjustment is performed to make the region
1147 * fit in the low memory. Otherwise the adjustment is
1148 * performed to make the region fit in the high memory.
1149 * @param basep[inout] Pointer to a variable which contains the region's base
1150 * address and which may receive the adjusted base address.
1151 * @param sizep[inout] Pointer to a variable which contains the region's size
1152 * and which may receive the adjusted size.
1153 *
1154 * @return True if the region still exists even after the adjustment.
1155 * @return False otherwise.
1156 *
1157 */
1158bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
1159{
1160 uintptr_t limit = KA2PA(config.identity_base) + config.identity_size;
1161
1162 if (low) {
1163 if (*basep > limit)
1164 return false;
1165
1166 if (*basep + *sizep > limit)
1167 *sizep = limit - *basep;
1168 } else {
1169 if (*basep + *sizep <= limit)
1170 return false;
1171
1172 if (*basep <= limit) {
1173 *sizep -= limit - *basep;
1174 *basep = limit;
1175 }
1176 }
1177
1178 return true;
1179}
1180
1181/** Return total size of all zones.
1182 *
1183 */
1184uint64_t zones_total_size(void)
1185{
1186 irq_spinlock_lock(&zones.lock, true);
1187
1188 uint64_t total = 0;
1189
1190 for (size_t i = 0; i < zones.count; i++)
1191 total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1192
1193 irq_spinlock_unlock(&zones.lock, true);
1194
1195 return total;
1196}
1197
1198void zones_stats(uint64_t *total, uint64_t *unavail, uint64_t *busy,
1199 uint64_t *free)
1200{
1201 assert(total != NULL);
1202 assert(unavail != NULL);
1203 assert(busy != NULL);
1204 assert(free != NULL);
1205
1206 irq_spinlock_lock(&zones.lock, true);
1207
1208 *total = 0;
1209 *unavail = 0;
1210 *busy = 0;
1211 *free = 0;
1212
1213 for (size_t i = 0; i < zones.count; i++) {
1214 *total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1215
1216 if (zones.info[i].flags & ZONE_AVAILABLE) {
1217 *busy += (uint64_t) FRAMES2SIZE(zones.info[i].busy_count);
1218 *free += (uint64_t) FRAMES2SIZE(zones.info[i].free_count);
1219 } else
1220 *unavail += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1221 }
1222
1223 irq_spinlock_unlock(&zones.lock, true);
1224}
1225
1226/** Prints list of zones.
1227 *
1228 */
1229void zones_print_list(void)
1230{
1231#ifdef __32_BITS__
1232 printf("[nr] [base addr] [frames ] [flags ] [free frames ] [busy frames ]\n");
1233#endif
1234
1235#ifdef __64_BITS__
1236 printf("[nr] [base address ] [frames ] [flags ] [free frames ] [busy frames ]\n");
1237#endif
1238
1239 /*
1240 * Because printing may require allocation of memory, we may not hold
1241 * the frame allocator locks when printing zone statistics. Therefore,
1242 * we simply gather the statistics under the protection of the locks and
1243 * print the statistics when the locks have been released.
1244 *
1245 * When someone adds/removes zones while we are printing the statistics,
1246 * we may end up with inaccurate output (e.g. a zone being skipped from
1247 * the listing).
1248 */
1249
1250 size_t free_lowmem = 0;
1251 size_t free_highmem = 0;
1252 size_t free_highprio = 0;
1253
1254 for (size_t i = 0; ; i++) {
1255 irq_spinlock_lock(&zones.lock, true);
1256
1257 if (i >= zones.count) {
1258 irq_spinlock_unlock(&zones.lock, true);
1259 break;
1260 }
1261
1262 pfn_t fbase = zones.info[i].base;
1263 uintptr_t base = PFN2ADDR(fbase);
1264 size_t count = zones.info[i].count;
1265 zone_flags_t flags = zones.info[i].flags;
1266 size_t free_count = zones.info[i].free_count;
1267 size_t busy_count = zones.info[i].busy_count;
1268
1269 bool available = ((flags & ZONE_AVAILABLE) != 0);
1270 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1271 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1272 bool highprio = is_high_priority(fbase, count);
1273
1274 if (available) {
1275 if (lowmem)
1276 free_lowmem += free_count;
1277
1278 if (highmem)
1279 free_highmem += free_count;
1280
1281 if (highprio) {
1282 free_highprio += free_count;
1283 } else {
1284 /*
1285 * Walk all frames of the zone and examine
1286 * all high priority memory to get accurate
1287 * statistics.
1288 */
1289
1290 for (size_t index = 0; index < count; index++) {
1291 if (is_high_priority(fbase + index, 0)) {
1292 if (!bitmap_get(&zones.info[i].bitmap, index))
1293 free_highprio++;
1294 } else
1295 break;
1296 }
1297 }
1298 }
1299
1300 irq_spinlock_unlock(&zones.lock, true);
1301
1302 printf("%-4zu", i);
1303
1304#ifdef __32_BITS__
1305 printf(" %p", (void *) base);
1306#endif
1307
1308#ifdef __64_BITS__
1309 printf(" %p", (void *) base);
1310#endif
1311
1312 printf(" %12zu %c%c%c%c%c ", count,
1313 available ? 'A' : '-',
1314 (flags & ZONE_RESERVED) ? 'R' : '-',
1315 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1316 (flags & ZONE_LOWMEM) ? 'L' : '-',
1317 (flags & ZONE_HIGHMEM) ? 'H' : '-');
1318
1319 if (available)
1320 printf("%14zu %14zu",
1321 free_count, busy_count);
1322
1323 printf("\n");
1324 }
1325
1326 printf("\n");
1327
1328 uint64_t size;
1329 const char *size_suffix;
1330
1331 bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
1332 false);
1333 printf("Available low memory: %zu frames (%" PRIu64 " %s)\n",
1334 free_lowmem, size, size_suffix);
1335
1336 bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
1337 false);
1338 printf("Available high memory: %zu frames (%" PRIu64 " %s)\n",
1339 free_highmem, size, size_suffix);
1340
1341 bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
1342 false);
1343 printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
1344 free_highprio, size, size_suffix);
1345}
1346
1347/** Prints zone details.
1348 *
1349 * @param num Zone base address or zone number.
1350 *
1351 */
1352void zone_print_one(size_t num)
1353{
1354 irq_spinlock_lock(&zones.lock, true);
1355 size_t znum = (size_t) -1;
1356
1357 for (size_t i = 0; i < zones.count; i++) {
1358 if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
1359 znum = i;
1360 break;
1361 }
1362 }
1363
1364 if (znum == (size_t) -1) {
1365 irq_spinlock_unlock(&zones.lock, true);
1366 printf("Zone not found.\n");
1367 return;
1368 }
1369
1370 size_t free_lowmem = 0;
1371 size_t free_highmem = 0;
1372 size_t free_highprio = 0;
1373
1374 pfn_t fbase = zones.info[znum].base;
1375 uintptr_t base = PFN2ADDR(fbase);
1376 zone_flags_t flags = zones.info[znum].flags;
1377 size_t count = zones.info[znum].count;
1378 size_t free_count = zones.info[znum].free_count;
1379 size_t busy_count = zones.info[znum].busy_count;
1380
1381 bool available = ((flags & ZONE_AVAILABLE) != 0);
1382 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1383 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1384 bool highprio = is_high_priority(fbase, count);
1385
1386 if (available) {
1387 if (lowmem)
1388 free_lowmem = free_count;
1389
1390 if (highmem)
1391 free_highmem = free_count;
1392
1393 if (highprio) {
1394 free_highprio = free_count;
1395 } else {
1396 /*
1397 * Walk all frames of the zone and examine
1398 * all high priority memory to get accurate
1399 * statistics.
1400 */
1401
1402 for (size_t index = 0; index < count; index++) {
1403 if (is_high_priority(fbase + index, 0)) {
1404 if (!bitmap_get(&zones.info[znum].bitmap, index))
1405 free_highprio++;
1406 } else
1407 break;
1408 }
1409 }
1410 }
1411
1412 irq_spinlock_unlock(&zones.lock, true);
1413
1414 uint64_t size;
1415 const char *size_suffix;
1416
1417 bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
1418
1419 printf("Zone number: %zu\n", znum);
1420 printf("Zone base address: %p\n", (void *) base);
1421 printf("Zone size: %zu frames (%" PRIu64 " %s)\n", count,
1422 size, size_suffix);
1423 printf("Zone flags: %c%c%c%c%c\n",
1424 available ? 'A' : '-',
1425 (flags & ZONE_RESERVED) ? 'R' : '-',
1426 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1427 (flags & ZONE_LOWMEM) ? 'L' : '-',
1428 (flags & ZONE_HIGHMEM) ? 'H' : '-');
1429
1430 if (available) {
1431 bin_order_suffix(FRAMES2SIZE(busy_count), &size, &size_suffix,
1432 false);
1433 printf("Allocated space: %zu frames (%" PRIu64 " %s)\n",
1434 busy_count, size, size_suffix);
1435
1436 bin_order_suffix(FRAMES2SIZE(free_count), &size, &size_suffix,
1437 false);
1438 printf("Available space: %zu frames (%" PRIu64 " %s)\n",
1439 free_count, size, size_suffix);
1440
1441 bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
1442 false);
1443 printf("Available low memory: %zu frames (%" PRIu64 " %s)\n",
1444 free_lowmem, size, size_suffix);
1445
1446 bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
1447 false);
1448 printf("Available high memory: %zu frames (%" PRIu64 " %s)\n",
1449 free_highmem, size, size_suffix);
1450
1451 bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
1452 false);
1453 printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
1454 free_highprio, size, size_suffix);
1455 }
1456}
1457
1458/** @}
1459 */
Note: See TracBrowser for help on using the repository browser.