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

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

Fix memory corruption caused by merging zones

Originally, the frames in the gap between the two merged zones has not
been marked as unavailable. This caused allocation requests to
potentially land in this gap and thus to memory corruption. Now the
frames in the gap are properly marked.

Use a dedicated function zone_mark_available() to mark old
configuration frames after merging as available. Since the
configuration frames are either marked using zone_mark_unavailable()
(for the original zones created according to the physical memory map)
or the busy count of the configuration frames is explicitly extracted
during the merging, the use of zone_frame_free() caused underflow of
the busy counter.

Add a few useful assertions.

  • 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 zone_mark_unavailable(&zones.info[z1], old_z1->count + i);
522 }
523}
524
525/** Return old configuration frames into the zone.
526 *
527 * We have two cases:
528 * - The configuration data is outside the zone
529 * -> do nothing (perhaps call frame_free?)
530 * - The configuration data was created by zone_create
531 * or updated by reduce_region -> free every frame
532 *
533 * @param znum The actual zone where freeing should occur.
534 * @param pfn Old zone configuration frame.
535 * @param count Old zone frame count.
536 *
537 */
538_NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
539{
540 assert(zones.info[znum].flags & ZONE_AVAILABLE);
541
542 size_t cframes = SIZE2FRAMES(zone_conf_size(count));
543
544 if ((pfn < zones.info[znum].base) ||
545 (pfn >= zones.info[znum].base + zones.info[znum].count))
546 return;
547
548 for (size_t i = 0; i < cframes; i++)
549 zone_mark_available(&zones.info[znum],
550 pfn - zones.info[znum].base + i);
551}
552
553/** Merge zones z1 and z2.
554 *
555 * The merged zones must be 2 zones with no zone existing in between
556 * (which means that z2 = z1 + 1). Both zones must be available zones
557 * with the same flags.
558 *
559 * When you create a new zone, the frame allocator configuration does
560 * not to be 2^order size. Once the allocator is running it is no longer
561 * possible, merged configuration data occupies more space :-/
562 *
563 */
564bool zone_merge(size_t z1, size_t z2)
565{
566 irq_spinlock_lock(&zones.lock, true);
567
568 bool ret = true;
569
570 /*
571 * We can join only 2 zones with none existing inbetween,
572 * the zones have to be available and with the same
573 * set of flags
574 */
575 if ((z1 >= zones.count) || (z2 >= zones.count) || (z2 - z1 != 1) ||
576 (zones.info[z1].flags != zones.info[z2].flags)) {
577 ret = false;
578 goto errout;
579 }
580
581 pfn_t cframes = SIZE2FRAMES(zone_conf_size(
582 zones.info[z2].base - zones.info[z1].base +
583 zones.info[z2].count));
584
585 /* Allocate merged zone data inside one of the zones */
586 pfn_t pfn;
587 if (zone_can_alloc(&zones.info[z1], cframes, 0)) {
588 pfn = zones.info[z1].base +
589 zone_frame_alloc(&zones.info[z1], cframes, 0);
590 } else if (zone_can_alloc(&zones.info[z2], cframes, 0)) {
591 pfn = zones.info[z2].base +
592 zone_frame_alloc(&zones.info[z2], cframes, 0);
593 } else {
594 ret = false;
595 goto errout;
596 }
597
598 /* Preserve original data from z1 */
599 zone_t old_z1 = zones.info[z1];
600
601 /* Do zone merging */
602 zone_merge_internal(z1, z2, &old_z1, (void *) PA2KA(PFN2ADDR(pfn)));
603
604 /* Subtract zone information from busy frames */
605 zones.info[z1].busy_count -= cframes;
606
607 /* Free old zone information */
608 return_config_frames(z1,
609 ADDR2PFN(KA2PA((uintptr_t) old_z1.frames)), old_z1.count);
610 return_config_frames(z1,
611 ADDR2PFN(KA2PA((uintptr_t) zones.info[z2].frames)),
612 zones.info[z2].count);
613
614 /* Move zones down */
615 for (size_t i = z2 + 1; i < zones.count; i++)
616 zones.info[i - 1] = zones.info[i];
617
618 zones.count--;
619
620errout:
621 irq_spinlock_unlock(&zones.lock, true);
622
623 return ret;
624}
625
626/** Merge all mergeable zones into one big zone.
627 *
628 * It is reasonable to do this on systems where
629 * BIOS reports parts in chunks, so that we could
630 * have 1 zone (it's faster).
631 *
632 */
633void zone_merge_all(void)
634{
635 size_t i = 1;
636
637 while (i < zones.count) {
638 if (!zone_merge(i - 1, i))
639 i++;
640 }
641}
642
643/** Create new frame zone.
644 *
645 * @param zone Zone to construct.
646 * @param start Physical address of the first frame within the zone.
647 * @param count Count of frames in zone.
648 * @param flags Zone flags.
649 * @param confdata Configuration data of the zone.
650 *
651 * @return Initialized zone.
652 *
653 */
654_NO_TRACE static void zone_construct(zone_t *zone, pfn_t start, size_t count,
655 zone_flags_t flags, void *confdata)
656{
657 zone->base = start;
658 zone->count = count;
659 zone->flags = flags;
660 zone->free_count = count;
661 zone->busy_count = 0;
662
663 if (flags & ZONE_AVAILABLE) {
664 /*
665 * Initialize frame bitmap (located after the array of
666 * frame_t structures in the configuration space).
667 */
668
669 bitmap_initialize(&zone->bitmap, count, confdata +
670 (sizeof(frame_t) * count));
671 bitmap_clear_range(&zone->bitmap, 0, count);
672
673 /*
674 * Initialize the array of frame_t structures.
675 */
676
677 zone->frames = (frame_t *) confdata;
678
679 for (size_t i = 0; i < count; i++)
680 frame_initialize(&zone->frames[i]);
681 } else {
682 bitmap_initialize(&zone->bitmap, 0, NULL);
683 zone->frames = NULL;
684 }
685}
686
687/** Compute configuration data size for zone.
688 *
689 * @param count Size of zone in frames.
690 *
691 * @return Size of zone configuration info (in bytes).
692 *
693 */
694size_t zone_conf_size(size_t count)
695{
696 return (count * sizeof(frame_t) + bitmap_size(count));
697}
698
699/** Allocate external configuration frames from low memory. */
700pfn_t zone_external_conf_alloc(size_t count)
701{
702 size_t frames = SIZE2FRAMES(zone_conf_size(count));
703
704 return ADDR2PFN((uintptr_t)
705 frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
706}
707
708/** Create and add zone to system.
709 *
710 * @param start First frame number (absolute).
711 * @param count Size of zone in frames.
712 * @param confframe Where configuration frames are supposed to be.
713 * Automatically checks that we will not disturb the
714 * kernel and possibly init. If confframe is given
715 * _outside_ this zone, it is expected, that the area is
716 * already marked BUSY and big enough to contain
717 * zone_conf_size() amount of data. If the confframe is
718 * inside the area, the zone free frame information is
719 * modified not to include it.
720 *
721 * @return Zone number or -1 on error.
722 *
723 */
724size_t zone_create(pfn_t start, size_t count, pfn_t confframe,
725 zone_flags_t flags)
726{
727 irq_spinlock_lock(&zones.lock, true);
728
729 if (flags & ZONE_AVAILABLE) { /* Create available zone */
730 /*
731 * Theoretically we could have NULL here, practically make sure
732 * nobody tries to do that. If some platform requires, remove
733 * the assert
734 */
735 assert(confframe != ADDR2PFN((uintptr_t) NULL));
736
737 /* Update the known end of physical memory. */
738 config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
739
740 /*
741 * If confframe is supposed to be inside our zone, then make sure
742 * it does not span kernel & init
743 */
744 size_t confcount = SIZE2FRAMES(zone_conf_size(count));
745
746 if ((confframe >= start) && (confframe < start + count)) {
747 for (; confframe < start + count; confframe++) {
748 uintptr_t addr = PFN2ADDR(confframe);
749 if (overlaps(addr, PFN2ADDR(confcount),
750 KA2PA(config.base), config.kernel_size))
751 continue;
752
753 if (overlaps(addr, PFN2ADDR(confcount),
754 KA2PA(config.stack_base), config.stack_size))
755 continue;
756
757 bool overlap = false;
758 for (size_t i = 0; i < init.cnt; i++) {
759 if (overlaps(addr, PFN2ADDR(confcount),
760 init.tasks[i].paddr,
761 init.tasks[i].size)) {
762 overlap = true;
763 break;
764 }
765 }
766
767 if (overlap)
768 continue;
769
770 break;
771 }
772
773 if (confframe >= start + count)
774 panic("Cannot find configuration data for zone.");
775 }
776
777 size_t znum = zones_insert_zone(start, count, flags);
778 if (znum == (size_t) -1) {
779 irq_spinlock_unlock(&zones.lock, true);
780 return (size_t) -1;
781 }
782
783 void *confdata = (void *) PA2KA(PFN2ADDR(confframe));
784 zone_construct(&zones.info[znum], start, count, flags, confdata);
785
786 /* If confdata in zone, mark as unavailable */
787 if ((confframe >= start) && (confframe < start + count)) {
788 for (size_t i = confframe; i < confframe + confcount; i++)
789 zone_mark_unavailable(&zones.info[znum],
790 i - zones.info[znum].base);
791 }
792
793 irq_spinlock_unlock(&zones.lock, true);
794
795 return znum;
796 }
797
798 /* Non-available zone */
799 size_t znum = zones_insert_zone(start, count, flags);
800 if (znum == (size_t) -1) {
801 irq_spinlock_unlock(&zones.lock, true);
802 return (size_t) -1;
803 }
804
805 zone_construct(&zones.info[znum], start, count, flags, NULL);
806
807 irq_spinlock_unlock(&zones.lock, true);
808
809 return znum;
810}
811
812/*
813 * Frame functions
814 */
815
816/** Set parent of frame. */
817void frame_set_parent(pfn_t pfn, void *data, size_t hint)
818{
819 irq_spinlock_lock(&zones.lock, true);
820
821 size_t znum = find_zone(pfn, 1, hint);
822
823 assert(znum != (size_t) -1);
824
825 zone_get_frame(&zones.info[znum],
826 pfn - zones.info[znum].base)->parent = data;
827
828 irq_spinlock_unlock(&zones.lock, true);
829}
830
831void *frame_get_parent(pfn_t pfn, size_t hint)
832{
833 irq_spinlock_lock(&zones.lock, true);
834
835 size_t znum = find_zone(pfn, 1, hint);
836
837 assert(znum != (size_t) -1);
838
839 void *res = zone_get_frame(&zones.info[znum],
840 pfn - zones.info[znum].base)->parent;
841
842 irq_spinlock_unlock(&zones.lock, true);
843
844 return res;
845}
846
847static size_t try_find_zone(size_t count, bool lowmem,
848 pfn_t frame_constraint, size_t hint)
849{
850 if (!lowmem) {
851 size_t znum = find_free_zone(count,
852 ZONE_HIGHMEM | ZONE_AVAILABLE, frame_constraint, hint);
853 if (znum != (size_t) -1)
854 return znum;
855 }
856
857 return find_free_zone(count, ZONE_LOWMEM | ZONE_AVAILABLE,
858 frame_constraint, hint);
859}
860
861/** Allocate frames of physical memory.
862 *
863 * @param count Number of continuous frames to allocate.
864 * @param flags Flags for host zone selection and address processing.
865 * @param constraint Indication of physical address bits that cannot be
866 * set in the address of the first allocated frame.
867 * @param pzone Preferred zone.
868 *
869 * @return Physical address of the allocated frame.
870 *
871 */
872uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
873 uintptr_t constraint, size_t *pzone)
874{
875 assert(count > 0);
876
877 size_t hint = pzone ? (*pzone) : 0;
878 pfn_t frame_constraint = ADDR2PFN(constraint);
879
880 /*
881 * If not told otherwise, we must first reserve the memory.
882 */
883 if (!(flags & FRAME_NO_RESERVE))
884 reserve_force_alloc(count);
885
886loop:
887 irq_spinlock_lock(&zones.lock, true);
888
889 // TODO: Print diagnostic if neither is explicitly specified.
890 bool lowmem = (flags & FRAME_LOWMEM) || !(flags & FRAME_HIGHMEM);
891
892 /*
893 * First, find suitable frame zone.
894 */
895 size_t znum = try_find_zone(count, lowmem, frame_constraint, hint);
896
897 /*
898 * If no memory, reclaim some slab memory,
899 * if it does not help, reclaim all.
900 */
901 if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
902 irq_spinlock_unlock(&zones.lock, true);
903 size_t freed = slab_reclaim(0);
904 irq_spinlock_lock(&zones.lock, true);
905
906 if (freed > 0)
907 znum = try_find_zone(count, lowmem,
908 frame_constraint, hint);
909
910 if (znum == (size_t) -1) {
911 irq_spinlock_unlock(&zones.lock, true);
912 freed = slab_reclaim(SLAB_RECLAIM_ALL);
913 irq_spinlock_lock(&zones.lock, true);
914
915 if (freed > 0)
916 znum = try_find_zone(count, lowmem,
917 frame_constraint, hint);
918 }
919 }
920
921 if (znum == (size_t) -1) {
922 if (flags & FRAME_ATOMIC) {
923 irq_spinlock_unlock(&zones.lock, true);
924
925 if (!(flags & FRAME_NO_RESERVE))
926 reserve_free(count);
927
928 return 0;
929 }
930
931 size_t avail = frame_total_free_get_internal();
932
933 irq_spinlock_unlock(&zones.lock, true);
934
935 if (!THREAD)
936 panic("Cannot wait for %zu frames to become available "
937 "(%zu available).", count, avail);
938
939 /*
940 * Sleep until some frames are available again.
941 */
942
943#ifdef CONFIG_DEBUG
944 log(LF_OTHER, LVL_DEBUG,
945 "Thread %" PRIu64 " waiting for %zu frames "
946 "%zu available.", THREAD->tid, count, avail);
947#endif
948
949 /*
950 * Since the mem_avail_mtx is an active mutex, we need to
951 * disable interrupts to prevent deadlock with TLB shootdown.
952 */
953 ipl_t ipl = interrupts_disable();
954 mutex_lock(&mem_avail_mtx);
955
956 if (mem_avail_req > 0)
957 mem_avail_req = min(mem_avail_req, count);
958 else
959 mem_avail_req = count;
960
961 size_t gen = mem_avail_gen;
962
963 while (gen == mem_avail_gen)
964 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
965
966 mutex_unlock(&mem_avail_mtx);
967 interrupts_restore(ipl);
968
969#ifdef CONFIG_DEBUG
970 log(LF_OTHER, LVL_DEBUG, "Thread %" PRIu64 " woken up.",
971 THREAD->tid);
972#endif
973
974 goto loop;
975 }
976
977 pfn_t pfn = zone_frame_alloc(&zones.info[znum], count,
978 frame_constraint) + zones.info[znum].base;
979
980 irq_spinlock_unlock(&zones.lock, true);
981
982 if (pzone)
983 *pzone = znum;
984
985 return PFN2ADDR(pfn);
986}
987
988uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
989{
990 return frame_alloc_generic(count, flags, constraint, NULL);
991}
992
993/** Free frames of physical memory.
994 *
995 * Find respective frame structures for supplied physical frames.
996 * Decrement each frame reference count. If it drops to zero, mark
997 * the frames as available.
998 *
999 * @param start Physical Address of the first frame to be freed.
1000 * @param count Number of frames to free.
1001 * @param flags Flags to control memory reservation.
1002 *
1003 */
1004void frame_free_generic(uintptr_t start, size_t count, frame_flags_t flags)
1005{
1006 size_t freed = 0;
1007
1008 irq_spinlock_lock(&zones.lock, true);
1009
1010 for (size_t i = 0; i < count; i++) {
1011 /*
1012 * First, find host frame zone for addr.
1013 */
1014 pfn_t pfn = ADDR2PFN(start) + i;
1015 size_t znum = find_zone(pfn, 1, 0);
1016
1017 assert(znum != (size_t) -1);
1018
1019 freed += zone_frame_free(&zones.info[znum],
1020 pfn - zones.info[znum].base);
1021 }
1022
1023 irq_spinlock_unlock(&zones.lock, true);
1024
1025 /*
1026 * Signal that some memory has been freed.
1027 * Since the mem_avail_mtx is an active mutex,
1028 * we need to disable interruptsto prevent deadlock
1029 * with TLB shootdown.
1030 */
1031
1032 ipl_t ipl = interrupts_disable();
1033 mutex_lock(&mem_avail_mtx);
1034
1035 if (mem_avail_req > 0)
1036 mem_avail_req -= min(mem_avail_req, freed);
1037
1038 if (mem_avail_req == 0) {
1039 mem_avail_gen++;
1040 condvar_broadcast(&mem_avail_cv);
1041 }
1042
1043 mutex_unlock(&mem_avail_mtx);
1044 interrupts_restore(ipl);
1045
1046 if (!(flags & FRAME_NO_RESERVE))
1047 reserve_free(freed);
1048}
1049
1050void frame_free(uintptr_t frame, size_t count)
1051{
1052 frame_free_generic(frame, count, 0);
1053}
1054
1055void frame_free_noreserve(uintptr_t frame, size_t count)
1056{
1057 frame_free_generic(frame, count, FRAME_NO_RESERVE);
1058}
1059
1060/** Add reference to frame.
1061 *
1062 * Find respective frame structure for supplied PFN and
1063 * increment frame reference count.
1064 *
1065 * @param pfn Frame number of the frame to be freed.
1066 *
1067 */
1068_NO_TRACE void frame_reference_add(pfn_t pfn)
1069{
1070 irq_spinlock_lock(&zones.lock, true);
1071
1072 /*
1073 * First, find host frame zone for addr.
1074 */
1075 size_t znum = find_zone(pfn, 1, 0);
1076
1077 assert(znum != (size_t) -1);
1078
1079 zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
1080
1081 irq_spinlock_unlock(&zones.lock, true);
1082}
1083
1084/** Mark given range unavailable in frame zones.
1085 *
1086 */
1087_NO_TRACE void frame_mark_unavailable(pfn_t start, size_t count)
1088{
1089 irq_spinlock_lock(&zones.lock, true);
1090
1091 for (size_t i = 0; i < count; i++) {
1092 size_t znum = find_zone(start + i, 1, 0);
1093
1094 if (znum == (size_t) -1) /* PFN not found */
1095 continue;
1096
1097 zone_mark_unavailable(&zones.info[znum],
1098 start + i - zones.info[znum].base);
1099 }
1100
1101 irq_spinlock_unlock(&zones.lock, true);
1102}
1103
1104/** Initialize physical memory management.
1105 *
1106 */
1107void frame_init(void)
1108{
1109 if (config.cpu_active == 1) {
1110 zones.count = 0;
1111 irq_spinlock_initialize(&zones.lock, "frame.zones.lock");
1112 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1113 condvar_initialize(&mem_avail_cv);
1114 }
1115
1116 /* Tell the architecture to create some memory */
1117 frame_low_arch_init();
1118
1119 if (config.cpu_active == 1) {
1120 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1121 SIZE2FRAMES(config.kernel_size));
1122 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
1123 SIZE2FRAMES(config.stack_size));
1124
1125 for (size_t i = 0; i < init.cnt; i++)
1126 frame_mark_unavailable(ADDR2PFN(init.tasks[i].paddr),
1127 SIZE2FRAMES(init.tasks[i].size));
1128
1129 if (ballocs.size)
1130 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1131 SIZE2FRAMES(ballocs.size));
1132
1133 /*
1134 * Blacklist first frame, as allocating NULL would
1135 * fail in some places
1136 */
1137 frame_mark_unavailable(0, 1);
1138 }
1139
1140 frame_high_arch_init();
1141}
1142
1143/** Adjust bounds of physical memory region according to low/high memory split.
1144 *
1145 * @param low[in] If true, the adjustment is performed to make the region
1146 * fit in the low memory. Otherwise the adjustment is
1147 * performed to make the region fit in the high memory.
1148 * @param basep[inout] Pointer to a variable which contains the region's base
1149 * address and which may receive the adjusted base address.
1150 * @param sizep[inout] Pointer to a variable which contains the region's size
1151 * and which may receive the adjusted size.
1152 *
1153 * @return True if the region still exists even after the adjustment.
1154 * @return False otherwise.
1155 *
1156 */
1157bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
1158{
1159 uintptr_t limit = KA2PA(config.identity_base) + config.identity_size;
1160
1161 if (low) {
1162 if (*basep > limit)
1163 return false;
1164
1165 if (*basep + *sizep > limit)
1166 *sizep = limit - *basep;
1167 } else {
1168 if (*basep + *sizep <= limit)
1169 return false;
1170
1171 if (*basep <= limit) {
1172 *sizep -= limit - *basep;
1173 *basep = limit;
1174 }
1175 }
1176
1177 return true;
1178}
1179
1180/** Return total size of all zones.
1181 *
1182 */
1183uint64_t zones_total_size(void)
1184{
1185 irq_spinlock_lock(&zones.lock, true);
1186
1187 uint64_t total = 0;
1188
1189 for (size_t i = 0; i < zones.count; i++)
1190 total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1191
1192 irq_spinlock_unlock(&zones.lock, true);
1193
1194 return total;
1195}
1196
1197void zones_stats(uint64_t *total, uint64_t *unavail, uint64_t *busy,
1198 uint64_t *free)
1199{
1200 assert(total != NULL);
1201 assert(unavail != NULL);
1202 assert(busy != NULL);
1203 assert(free != NULL);
1204
1205 irq_spinlock_lock(&zones.lock, true);
1206
1207 *total = 0;
1208 *unavail = 0;
1209 *busy = 0;
1210 *free = 0;
1211
1212 for (size_t i = 0; i < zones.count; i++) {
1213 *total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1214
1215 if (zones.info[i].flags & ZONE_AVAILABLE) {
1216 *busy += (uint64_t) FRAMES2SIZE(zones.info[i].busy_count);
1217 *free += (uint64_t) FRAMES2SIZE(zones.info[i].free_count);
1218 } else
1219 *unavail += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1220 }
1221
1222 irq_spinlock_unlock(&zones.lock, true);
1223}
1224
1225/** Prints list of zones.
1226 *
1227 */
1228void zones_print_list(void)
1229{
1230#ifdef __32_BITS__
1231 printf("[nr] [base addr] [frames ] [flags ] [free frames ] [busy frames ]\n");
1232#endif
1233
1234#ifdef __64_BITS__
1235 printf("[nr] [base address ] [frames ] [flags ] [free frames ] [busy frames ]\n");
1236#endif
1237
1238 /*
1239 * Because printing may require allocation of memory, we may not hold
1240 * the frame allocator locks when printing zone statistics. Therefore,
1241 * we simply gather the statistics under the protection of the locks and
1242 * print the statistics when the locks have been released.
1243 *
1244 * When someone adds/removes zones while we are printing the statistics,
1245 * we may end up with inaccurate output (e.g. a zone being skipped from
1246 * the listing).
1247 */
1248
1249 size_t free_lowmem = 0;
1250 size_t free_highmem = 0;
1251 size_t free_highprio = 0;
1252
1253 for (size_t i = 0; ; i++) {
1254 irq_spinlock_lock(&zones.lock, true);
1255
1256 if (i >= zones.count) {
1257 irq_spinlock_unlock(&zones.lock, true);
1258 break;
1259 }
1260
1261 pfn_t fbase = zones.info[i].base;
1262 uintptr_t base = PFN2ADDR(fbase);
1263 size_t count = zones.info[i].count;
1264 zone_flags_t flags = zones.info[i].flags;
1265 size_t free_count = zones.info[i].free_count;
1266 size_t busy_count = zones.info[i].busy_count;
1267
1268 bool available = ((flags & ZONE_AVAILABLE) != 0);
1269 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1270 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1271 bool highprio = is_high_priority(fbase, count);
1272
1273 if (available) {
1274 if (lowmem)
1275 free_lowmem += free_count;
1276
1277 if (highmem)
1278 free_highmem += free_count;
1279
1280 if (highprio) {
1281 free_highprio += free_count;
1282 } else {
1283 /*
1284 * Walk all frames of the zone and examine
1285 * all high priority memory to get accurate
1286 * statistics.
1287 */
1288
1289 for (size_t index = 0; index < count; index++) {
1290 if (is_high_priority(fbase + index, 0)) {
1291 if (!bitmap_get(&zones.info[i].bitmap, index))
1292 free_highprio++;
1293 } else
1294 break;
1295 }
1296 }
1297 }
1298
1299 irq_spinlock_unlock(&zones.lock, true);
1300
1301 printf("%-4zu", i);
1302
1303#ifdef __32_BITS__
1304 printf(" %p", (void *) base);
1305#endif
1306
1307#ifdef __64_BITS__
1308 printf(" %p", (void *) base);
1309#endif
1310
1311 printf(" %12zu %c%c%c%c%c ", count,
1312 available ? 'A' : '-',
1313 (flags & ZONE_RESERVED) ? 'R' : '-',
1314 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1315 (flags & ZONE_LOWMEM) ? 'L' : '-',
1316 (flags & ZONE_HIGHMEM) ? 'H' : '-');
1317
1318 if (available)
1319 printf("%14zu %14zu",
1320 free_count, busy_count);
1321
1322 printf("\n");
1323 }
1324
1325 printf("\n");
1326
1327 uint64_t size;
1328 const char *size_suffix;
1329
1330 bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
1331 false);
1332 printf("Available low memory: %zu frames (%" PRIu64 " %s)\n",
1333 free_lowmem, size, size_suffix);
1334
1335 bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
1336 false);
1337 printf("Available high memory: %zu frames (%" PRIu64 " %s)\n",
1338 free_highmem, size, size_suffix);
1339
1340 bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
1341 false);
1342 printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
1343 free_highprio, size, size_suffix);
1344}
1345
1346/** Prints zone details.
1347 *
1348 * @param num Zone base address or zone number.
1349 *
1350 */
1351void zone_print_one(size_t num)
1352{
1353 irq_spinlock_lock(&zones.lock, true);
1354 size_t znum = (size_t) -1;
1355
1356 for (size_t i = 0; i < zones.count; i++) {
1357 if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
1358 znum = i;
1359 break;
1360 }
1361 }
1362
1363 if (znum == (size_t) -1) {
1364 irq_spinlock_unlock(&zones.lock, true);
1365 printf("Zone not found.\n");
1366 return;
1367 }
1368
1369 size_t free_lowmem = 0;
1370 size_t free_highmem = 0;
1371 size_t free_highprio = 0;
1372
1373 pfn_t fbase = zones.info[znum].base;
1374 uintptr_t base = PFN2ADDR(fbase);
1375 zone_flags_t flags = zones.info[znum].flags;
1376 size_t count = zones.info[znum].count;
1377 size_t free_count = zones.info[znum].free_count;
1378 size_t busy_count = zones.info[znum].busy_count;
1379
1380 bool available = ((flags & ZONE_AVAILABLE) != 0);
1381 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1382 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1383 bool highprio = is_high_priority(fbase, count);
1384
1385 if (available) {
1386 if (lowmem)
1387 free_lowmem = free_count;
1388
1389 if (highmem)
1390 free_highmem = free_count;
1391
1392 if (highprio) {
1393 free_highprio = free_count;
1394 } else {
1395 /*
1396 * Walk all frames of the zone and examine
1397 * all high priority memory to get accurate
1398 * statistics.
1399 */
1400
1401 for (size_t index = 0; index < count; index++) {
1402 if (is_high_priority(fbase + index, 0)) {
1403 if (!bitmap_get(&zones.info[znum].bitmap, index))
1404 free_highprio++;
1405 } else
1406 break;
1407 }
1408 }
1409 }
1410
1411 irq_spinlock_unlock(&zones.lock, true);
1412
1413 uint64_t size;
1414 const char *size_suffix;
1415
1416 bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
1417
1418 printf("Zone number: %zu\n", znum);
1419 printf("Zone base address: %p\n", (void *) base);
1420 printf("Zone size: %zu frames (%" PRIu64 " %s)\n", count,
1421 size, size_suffix);
1422 printf("Zone flags: %c%c%c%c%c\n",
1423 available ? 'A' : '-',
1424 (flags & ZONE_RESERVED) ? 'R' : '-',
1425 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1426 (flags & ZONE_LOWMEM) ? 'L' : '-',
1427 (flags & ZONE_HIGHMEM) ? 'H' : '-');
1428
1429 if (available) {
1430 bin_order_suffix(FRAMES2SIZE(busy_count), &size, &size_suffix,
1431 false);
1432 printf("Allocated space: %zu frames (%" PRIu64 " %s)\n",
1433 busy_count, size, size_suffix);
1434
1435 bin_order_suffix(FRAMES2SIZE(free_count), &size, &size_suffix,
1436 false);
1437 printf("Available space: %zu frames (%" PRIu64 " %s)\n",
1438 free_count, size, size_suffix);
1439
1440 bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
1441 false);
1442 printf("Available low memory: %zu frames (%" PRIu64 " %s)\n",
1443 free_lowmem, size, size_suffix);
1444
1445 bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
1446 false);
1447 printf("Available high memory: %zu frames (%" PRIu64 " %s)\n",
1448 free_highmem, size, size_suffix);
1449
1450 bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
1451 false);
1452 printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
1453 free_highprio, size, size_suffix);
1454 }
1455}
1456
1457/** @}
1458 */
Note: See TracBrowser for help on using the repository browser.