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
RevLine 
[f761f1eb]1/*
[df4ed85]2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
[5f0f29ce]4 * Copyright (c) 2009 Martin Decky
[f761f1eb]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
[174156fd]31/** @addtogroup kernel_generic_mm
[b45c443]32 * @{
33 */
34
[9179d0a]35/**
[b45c443]36 * @file
[5f0f29ce]37 * @brief Physical frame allocator.
[9179d0a]38 *
39 * This file contains the physical frame allocator and memory zone management.
[b0c2075]40 * The frame allocator is built on top of the two-level bitmap structure.
[9179d0a]41 *
42 */
43
[d99c1d2]44#include <typedefs.h>
[f761f1eb]45#include <mm/frame.h>
[89bcb520]46#include <mm/reserve.h>
[20d50a1]47#include <mm/as.h>
[f761f1eb]48#include <panic.h>
[63e27ef]49#include <assert.h>
[5c9a08b]50#include <adt/list.h>
[1a1744e]51#include <synch/mutex.h>
52#include <synch/condvar.h>
[18e0a6c]53#include <arch/asm.h>
[9c0a9b3]54#include <arch.h>
[bab75df6]55#include <stdio.h>
[b2fa1204]56#include <log.h>
[9ebc238]57#include <align.h>
[085d973]58#include <mm/slab.h>
[bb68433]59#include <bitops.h>
[93165be]60#include <macros.h>
[d630139]61#include <config.h>
[933cadf]62#include <str.h>
[1066041]63#include <proc/thread.h> /* THREAD */
[18e0a6c]64
[e49e234]65zones_t zones;
[f761f1eb]66
[1a1744e]67/*
68 * Synchronization primitives used to sleep when there is no memory
69 * available.
70 */
[da1bafb]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. */
[a294ad0]75
[deaf8d5]76/** Initialize frame structure.
[84dd253]77 *
[5f0f29ce]78 * @param frame Frame structure to be initialized.
79 *
[f761f1eb]80 */
[8df5f20]81_NO_TRACE static void frame_initialize(frame_t *frame)
[f761f1eb]82{
[b0c2075]83 frame->refcount = 0;
84 frame->parent = NULL;
[f761f1eb]85}
86
[d1582b50]87/*
88 * Zones functions
89 */
[085d973]90
[deaf8d5]91/** Insert-sort zone into zones list.
[bb68433]92 *
[5f0f29ce]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 *
[84dd253]101 */
[8df5f20]102_NO_TRACE static size_t zones_insert_zone(pfn_t base, size_t count,
[e2650d3]103 zone_flags_t flags)
[f761f1eb]104{
[71eef11]105 if (zones.count + 1 == ZONES_MAX) {
[b2fa1204]106 log(LF_OTHER, LVL_ERROR, "Maximum zone count %u exceeded!",
107 ZONES_MAX);
[98000fb]108 return (size_t) -1;
[71eef11]109 }
[a35b458]110
[98000fb]111 size_t i;
[b6b576c]112 for (i = 0; i < zones.count; i++) {
[5f0f29ce]113 /* Check for overlap */
[e2650d3]114 if (overlaps(zones.info[i].base, zones.info[i].count,
115 base, count)) {
[a35b458]116
[e2650d3]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 */
[a35b458]123
[e2650d3]124 if ((zones.info[i].flags != flags) ||
125 (!iswithin(zones.info[i].base, zones.info[i].count,
126 base, count))) {
[b2fa1204]127 log(LF_OTHER, LVL_WARN,
128 "Zone (%p, %p) overlaps "
129 "with previous zone (%p %p)!",
[7e752b2]130 (void *) PFN2ADDR(base), (void *) PFN2ADDR(count),
131 (void *) PFN2ADDR(zones.info[i].base),
132 (void *) PFN2ADDR(zones.info[i].count));
[e2650d3]133 }
[a35b458]134
[98000fb]135 return (size_t) -1;
[085d973]136 }
[5f0f29ce]137 if (base < zones.info[i].base)
[bb68433]138 break;
[085d973]139 }
[a35b458]140
[bb68433]141 /* Move other zones up */
[b0c2075]142 for (size_t j = zones.count; j > i; j--)
[e49e234]143 zones.info[j] = zones.info[j - 1];
[a35b458]144
[bb68433]145 zones.count++;
[a35b458]146
[bb68433]147 return i;
[f761f1eb]148}
[fcacfb7]149
[5f0f29ce]150/** Get total available frames.
151 *
152 * Assume interrupts are disabled and zones lock is
153 * locked.
[71eef11]154 *
[5f0f29ce]155 * @return Total number of available frames.
[71eef11]156 *
[eef75f6]157 */
[8df5f20]158_NO_TRACE static size_t frame_total_free_get_internal(void)
[085d973]159{
[98000fb]160 size_t total = 0;
161 size_t i;
[8d308b9]162
[5f0f29ce]163 for (i = 0; i < zones.count; i++)
164 total += zones.info[i].free_count;
[a35b458]165
[5f0f29ce]166 return total;
167}
[8d308b9]168
[8df5f20]169_NO_TRACE size_t frame_total_free_get(void)
[8d308b9]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
[e49e234]180/** Find a zone with a given frames.
[5f0f29ce]181 *
182 * Assume interrupts are disabled and zones lock is
183 * locked.
184 *
185 * @param frame Frame number contained in zone.
[e49e234]186 * @param count Number of frames to look for.
[5f0f29ce]187 * @param hint Used as zone hint.
188 *
189 * @return Zone index or -1 if not found.
190 *
191 */
[8df5f20]192_NO_TRACE size_t find_zone(pfn_t frame, size_t count, size_t hint)
[5f0f29ce]193{
[6c441cf8]194 if (hint >= zones.count)
[085d973]195 hint = 0;
[a35b458]196
[98000fb]197 size_t i = hint;
[085d973]198 do {
[3bacee1]199 if ((zones.info[i].base <= frame) &&
200 (zones.info[i].base + zones.info[i].count >= frame + count))
[5f0f29ce]201 return i;
[a35b458]202
[085d973]203 i++;
204 if (i >= zones.count)
205 i = 0;
[a35b458]206
[5f7a0ef]207 } while (i != hint);
[a35b458]208
[98000fb]209 return (size_t) -1;
[085d973]210}
[328f2934]211
[b0c2075]212/** @return True if zone can allocate specified number of frames */
[8df5f20]213_NO_TRACE static bool zone_can_alloc(zone_t *zone, size_t count,
[b0c2075]214 pfn_t constraint)
[bb68433]215{
[b0c2075]216 /*
217 * The function bitmap_allocate_range() does not modify
218 * the bitmap if the last argument is NULL.
219 */
[a35b458]220
[e6c4b94]221 return ((zone->flags & ZONE_AVAILABLE) &&
[b0c2075]222 bitmap_allocate_range(&zone->bitmap, count, zone->base,
[f72906c]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 */
[8df5f20]241_NO_TRACE static size_t find_free_zone_all(size_t count, zone_flags_t flags,
[f72906c]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;
[a35b458]246
[f72906c]247 /* Check whether the zone meets the search criteria. */
248 if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
249 continue;
[a35b458]250
[f72906c]251 /* Check if the zone can satisfy the allocation request. */
252 if (zone_can_alloc(&zones.info[i], count, constraint))
253 return i;
254 }
[a35b458]255
[f72906c]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 */
[8df5f20]267_NO_TRACE static bool is_high_priority(pfn_t base, size_t count)
[f72906c]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 */
[8df5f20]287_NO_TRACE static size_t find_free_zone_lowprio(size_t count, zone_flags_t flags,
[f72906c]288 pfn_t constraint, size_t hint)
[1b20da0]289{
[f72906c]290 for (size_t pos = 0; pos < zones.count; pos++) {
291 size_t i = (pos + hint) % zones.count;
[a35b458]292
[f72906c]293 /* Skip zones containing only high-priority memory. */
294 if (is_high_priority(zones.info[i].base, zones.info[i].count))
295 continue;
[a35b458]296
[f72906c]297 /* Check whether the zone meets the search criteria. */
298 if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
299 continue;
[a35b458]300
[f72906c]301 /* Check if the zone can satisfy the allocation request. */
302 if (zone_can_alloc(&zones.info[i], count, constraint))
303 return i;
304 }
[a35b458]305
[f72906c]306 return (size_t) -1;
[bb68433]307}
308
[b0c2075]309/** Find a zone that can allocate specified number of frames
[5f0f29ce]310 *
311 * Assume interrupts are disabled and zones lock is
312 * locked.
[fcacfb7]313 *
[b0c2075]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.
[f72906c]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.
[fcacfb7]322 *
323 */
[8df5f20]324_NO_TRACE static size_t find_free_zone(size_t count, zone_flags_t flags,
[b0c2075]325 pfn_t constraint, size_t hint)
[fcacfb7]326{
[085d973]327 if (hint >= zones.count)
328 hint = 0;
[a35b458]329
[f72906c]330 /*
331 * Prefer zones with low-priority memory over
332 * zones with high-priority memory.
333 */
[a35b458]334
[f72906c]335 size_t znum = find_free_zone_lowprio(count, flags, constraint, hint);
336 if (znum != (size_t) -1)
337 return znum;
[a35b458]338
[f72906c]339 /* Take all zones into account */
340 return find_free_zone_all(count, flags, constraint, hint);
[fcacfb7]341}
342
[d1582b50]343/*
344 * Zone functions
345 */
[6e8b3c8]346
[b0c2075]347/** Return frame from zone. */
[8df5f20]348_NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t index)
[bb68433]349{
[63e27ef]350 assert(index < zone->count);
[a35b458]351
[b0c2075]352 return &zone->frames[index];
[6e8b3c8]353}
354
[deaf8d5]355/** Allocate frame in particular zone.
[085d973]356 *
[5f0f29ce]357 * Assume zone is locked and is available for allocation.
[9a68b34d]358 * Panics if allocation is impossible.
359 *
[b0c2075]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.
[085d973]364 *
[5f0f29ce]365 * @return Frame index in zone.
[9a68b34d]366 *
[085d973]367 */
[8df5f20]368_NO_TRACE static size_t zone_frame_alloc(zone_t *zone, size_t count,
[b0c2075]369 pfn_t constraint)
[085d973]370{
[63e27ef]371 assert(zone->flags & ZONE_AVAILABLE);
[81d3612]372 assert(zone->free_count >= count);
[a35b458]373
[b0c2075]374 /* Allocate frames from zone */
[566da7f8]375 size_t index = (size_t) -1;
[b0c2075]376 int avail = bitmap_allocate_range(&zone->bitmap, count, zone->base,
[f72906c]377 FRAME_LOWPRIO, constraint, &index);
[a35b458]378
[0705fc5]379 (void) avail;
[63e27ef]380 assert(avail);
381 assert(index != (size_t) -1);
[a35b458]382
[b0c2075]383 /* Update frame reference count */
384 for (size_t i = 0; i < count; i++) {
385 frame_t *frame = zone_get_frame(zone, index + i);
[a35b458]386
[63e27ef]387 assert(frame->refcount == 0);
[b0c2075]388 frame->refcount = 1;
389 }
[a35b458]390
[085d973]391 /* Update zone information. */
[b0c2075]392 zone->free_count -= count;
393 zone->busy_count += count;
[a35b458]394
[b0c2075]395 return index;
[085d973]396}
397
[deaf8d5]398/** Free frame from zone.
[085d973]399 *
[5f0f29ce]400 * Assume zone is locked and is available for deallocation.
401 *
[b0c2075]402 * @param zone Pointer to zone from which the frame is to be freed.
403 * @param index Frame index relative to zone.
[eb3d379]404 *
[b0c2075]405 * @return Number of freed frames.
[89bcb520]406 *
[085d973]407 */
[8df5f20]408_NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t index)
[085d973]409{
[63e27ef]410 assert(zone->flags & ZONE_AVAILABLE);
[a35b458]411
[b0c2075]412 frame_t *frame = zone_get_frame(zone, index);
[63e27ef]413 assert(frame->refcount > 0);
[a35b458]414
[085d973]415 if (!--frame->refcount) {
[81d3612]416 assert(zone->busy_count > 0);
417
[64f3d3b]418 bitmap_set(&zone->bitmap, index, 0);
[a35b458]419
[d3dfa42]420 /* Update zone information. */
[b0c2075]421 zone->free_count++;
422 zone->busy_count--;
[a35b458]423
[5df1963]424 return 1;
[085d973]425 }
[a35b458]426
[5df1963]427 return 0;
[085d973]428}
429
[deaf8d5]430/** Mark frame in zone unavailable to allocation. */
[8df5f20]431_NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t index)
[085d973]432{
[63e27ef]433 assert(zone->flags & ZONE_AVAILABLE);
[a35b458]434
[b0c2075]435 frame_t *frame = zone_get_frame(zone, index);
[81d3612]436 assert(frame->refcount <= 1);
437
[b0c2075]438 if (frame->refcount > 0)
[bb68433]439 return;
[a35b458]440
[81d3612]441 assert(zone->free_count > 0);
442
[cd3b380]443 frame->refcount = 1;
[b0c2075]444 bitmap_set_range(&zone->bitmap, index, 1);
[a35b458]445
[085d973]446 zone->free_count--;
[d060900]447 reserve_force_alloc(1);
[085d973]448}
449
[81d3612]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
[5f0f29ce]464/** Merge two zones.
[bb68433]465 *
[5f0f29ce]466 * Assume z1 & z2 are locked and compatible and zones lock is
467 * locked.
[bb68433]468 *
[b0c2075]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.
[eb3d379]473 *
[bb68433]474 */
[8df5f20]475_NO_TRACE static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1,
[b0c2075]476 void *confdata)
[bb68433]477{
[63e27ef]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,
[5f0f29ce]483 zones.info[z2].base, zones.info[z2].count));
[a35b458]484
[5f0f29ce]485 /* Difference between zone bases */
486 pfn_t base_diff = zones.info[z2].base - zones.info[z1].base;
[81d3612]487 pfn_t gap = base_diff - zones.info[z1].count;
[a35b458]488
[5f0f29ce]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;
[a35b458]492
[b0c2075]493 bitmap_initialize(&zones.info[z1].bitmap, zones.info[z1].count,
[c5396c1]494 confdata + (sizeof(frame_t) * zones.info[z1].count));
[adb252c0]495 bitmap_clear_range(&zones.info[z1].bitmap, 0, zones.info[z1].count);
[a35b458]496
[b0c2075]497 zones.info[z1].frames = (frame_t *) confdata;
[a35b458]498
[b0c2075]499 /*
500 * Copy frames and bits from both zones to preserve parents, etc.
[bb68433]501 */
[a35b458]502
[b0c2075]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];
[bb68433]507 }
[a35b458]508
[b0c2075]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];
[bb68433]514 }
[81d3612]515
516 /*
517 * Mark the gap between the original zones as unavailable.
518 */
519
520 for (size_t i = 0; i < gap; i++) {
[5ecac7e]521 frame_initialize(&zones.info[z1].frames[old_z1->count + i]);
[81d3612]522 zone_mark_unavailable(&zones.info[z1], old_z1->count + i);
523 }
[bb68433]524}
525
[deaf8d5]526/** Return old configuration frames into the zone.
[bb68433]527 *
[5f0f29ce]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.
[874878a]537 *
[bb68433]538 */
[8df5f20]539_NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
[bb68433]540{
[63e27ef]541 assert(zones.info[znum].flags & ZONE_AVAILABLE);
[a35b458]542
[98000fb]543 size_t cframes = SIZE2FRAMES(zone_conf_size(count));
[a35b458]544
[b0c2075]545 if ((pfn < zones.info[znum].base) ||
546 (pfn >= zones.info[znum].base + zones.info[znum].count))
[bb68433]547 return;
[a35b458]548
[b0c2075]549 for (size_t i = 0; i < cframes; i++)
[81d3612]550 zone_mark_available(&zones.info[znum],
[5f0f29ce]551 pfn - zones.info[znum].base + i);
[874878a]552}
553
[deaf8d5]554/** Merge zones z1 and z2.
[bb68433]555 *
[5f0f29ce]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 *
[bb68433]564 */
[98000fb]565bool zone_merge(size_t z1, size_t z2)
[bb68433]566{
[da1bafb]567 irq_spinlock_lock(&zones.lock, true);
[a35b458]568
[5f0f29ce]569 bool ret = true;
[a35b458]570
[b0c2075]571 /*
572 * We can join only 2 zones with none existing inbetween,
[5f0f29ce]573 * the zones have to be available and with the same
574 * set of flags
575 */
[e6c4b94]576 if ((z1 >= zones.count) || (z2 >= zones.count) || (z2 - z1 != 1) ||
577 (zones.info[z1].flags != zones.info[z2].flags)) {
[5f0f29ce]578 ret = false;
[bb68433]579 goto errout;
[5f0f29ce]580 }
[a35b458]581
[5f0f29ce]582 pfn_t cframes = SIZE2FRAMES(zone_conf_size(
[3bacee1]583 zones.info[z2].base - zones.info[z1].base +
584 zones.info[z2].count));
[a35b458]585
[5f0f29ce]586 /* Allocate merged zone data inside one of the zones */
587 pfn_t pfn;
[b0c2075]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);
[5f0f29ce]594 } else {
595 ret = false;
596 goto errout;
597 }
[a35b458]598
[5f0f29ce]599 /* Preserve original data from z1 */
600 zone_t old_z1 = zones.info[z1];
[a35b458]601
[5f0f29ce]602 /* Do zone merging */
[b0c2075]603 zone_merge_internal(z1, z2, &old_z1, (void *) PA2KA(PFN2ADDR(pfn)));
[a35b458]604
[bb68433]605 /* Subtract zone information from busy frames */
[5f0f29ce]606 zones.info[z1].busy_count -= cframes;
[a35b458]607
[5f0f29ce]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);
[a35b458]614
[e49e234]615 /* Move zones down */
[b0c2075]616 for (size_t i = z2 + 1; i < zones.count; i++)
[b6b576c]617 zones.info[i - 1] = zones.info[i];
[a35b458]618
[bb68433]619 zones.count--;
[a35b458]620
[bb68433]621errout:
[da1bafb]622 irq_spinlock_unlock(&zones.lock, true);
[a35b458]623
[5f0f29ce]624 return ret;
[bb68433]625}
626
[5f0f29ce]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).
[bb68433]632 *
633 */
634void zone_merge_all(void)
635{
[b0c2075]636 size_t i = 1;
[a35b458]637
[5f0f29ce]638 while (i < zones.count) {
[b0c2075]639 if (!zone_merge(i - 1, i))
[5f0f29ce]640 i++;
[bb68433]641 }
642}
643
[deaf8d5]644/** Create new frame zone.
[085d973]645 *
[b0c2075]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.
[5f0f29ce]651 *
652 * @return Initialized zone.
[085d973]653 *
654 */
[8df5f20]655_NO_TRACE static void zone_construct(zone_t *zone, pfn_t start, size_t count,
[b0c2075]656 zone_flags_t flags, void *confdata)
[085d973]657{
[5f0f29ce]658 zone->base = start;
659 zone->count = count;
660 zone->flags = flags;
661 zone->free_count = count;
662 zone->busy_count = 0;
[a35b458]663
[e6c4b94]664 if (flags & ZONE_AVAILABLE) {
[5f0f29ce]665 /*
[b0c2075]666 * Initialize frame bitmap (located after the array of
667 * frame_t structures in the configuration space).
[5f0f29ce]668 */
[a35b458]669
[c5396c1]670 bitmap_initialize(&zone->bitmap, count, confdata +
671 (sizeof(frame_t) * count));
[adb252c0]672 bitmap_clear_range(&zone->bitmap, 0, count);
[a35b458]673
[b0c2075]674 /*
675 * Initialize the array of frame_t structures.
676 */
[a35b458]677
[b0c2075]678 zone->frames = (frame_t *) confdata;
[a35b458]679
[b0c2075]680 for (size_t i = 0; i < count; i++)
681 frame_initialize(&zone->frames[i]);
682 } else {
[c5396c1]683 bitmap_initialize(&zone->bitmap, 0, NULL);
[5f0f29ce]684 zone->frames = NULL;
[b0c2075]685 }
[085d973]686}
687
[deaf8d5]688/** Compute configuration data size for zone.
[eb3d379]689 *
[5f0f29ce]690 * @param count Size of zone in frames.
691 *
692 * @return Size of zone configuration info (in bytes).
693 *
[eb3d379]694 */
[bbfdf62]695size_t zone_conf_size(size_t count)
[085d973]696{
[c5396c1]697 return (count * sizeof(frame_t) + bitmap_size(count));
[085d973]698}
699
[8bdcffa]700/** Allocate external configuration frames from low memory. */
701pfn_t zone_external_conf_alloc(size_t count)
702{
[b0c2075]703 size_t frames = SIZE2FRAMES(zone_conf_size(count));
[a35b458]704
[b0c2075]705 return ADDR2PFN((uintptr_t)
706 frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
[8bdcffa]707}
708
[deaf8d5]709/** Create and add zone to system.
[085d973]710 *
[5f0f29ce]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.
[b0c2075]714 * Automatically checks that we will not disturb the
[5f0f29ce]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 *
[085d973]724 */
[da1bafb]725size_t zone_create(pfn_t start, size_t count, pfn_t confframe,
726 zone_flags_t flags)
[085d973]727{
[da1bafb]728 irq_spinlock_lock(&zones.lock, true);
[a35b458]729
[e6c4b94]730 if (flags & ZONE_AVAILABLE) { /* Create available zone */
[b0c2075]731 /*
732 * Theoretically we could have NULL here, practically make sure
[5f0f29ce]733 * nobody tries to do that. If some platform requires, remove
734 * the assert
735 */
[3bacee1]736 assert(confframe != ADDR2PFN((uintptr_t) NULL));
[a35b458]737
[40c8c17]738 /* Update the known end of physical memory. */
739 config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
[a35b458]740
[b0c2075]741 /*
742 * If confframe is supposed to be inside our zone, then make sure
[5f0f29ce]743 * it does not span kernel & init
744 */
[98000fb]745 size_t confcount = SIZE2FRAMES(zone_conf_size(count));
[a35b458]746
[5f0f29ce]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;
[a35b458]753
[4638401]754 if (overlaps(addr, PFN2ADDR(confcount),
[5f0f29ce]755 KA2PA(config.stack_base), config.stack_size))
756 continue;
[a35b458]757
[5f0f29ce]758 bool overlap = false;
[b0c2075]759 for (size_t i = 0; i < init.cnt; i++) {
[5f0f29ce]760 if (overlaps(addr, PFN2ADDR(confcount),
[32817cc]761 init.tasks[i].paddr,
[5f0f29ce]762 init.tasks[i].size)) {
763 overlap = true;
764 break;
765 }
[b0c2075]766 }
[a35b458]767
[5f0f29ce]768 if (overlap)
769 continue;
[a35b458]770
[5f0f29ce]771 break;
772 }
[a35b458]773
[b0c2075]774 if (confframe >= start + count)
775 panic("Cannot find configuration data for zone.");
[085d973]776 }
[a35b458]777
[e2650d3]778 size_t znum = zones_insert_zone(start, count, flags);
[98000fb]779 if (znum == (size_t) -1) {
[da1bafb]780 irq_spinlock_unlock(&zones.lock, true);
[98000fb]781 return (size_t) -1;
[5f0f29ce]782 }
[a35b458]783
[b0c2075]784 void *confdata = (void *) PA2KA(PFN2ADDR(confframe));
785 zone_construct(&zones.info[znum], start, count, flags, confdata);
[a35b458]786
[5f0f29ce]787 /* If confdata in zone, mark as unavailable */
788 if ((confframe >= start) && (confframe < start + count)) {
[b0c2075]789 for (size_t i = confframe; i < confframe + confcount; i++)
[5f0f29ce]790 zone_mark_unavailable(&zones.info[znum],
791 i - zones.info[znum].base);
[085d973]792 }
[a35b458]793
[da1bafb]794 irq_spinlock_unlock(&zones.lock, true);
[a35b458]795
[5f0f29ce]796 return znum;
797 }
[a35b458]798
[5f0f29ce]799 /* Non-available zone */
[e2650d3]800 size_t znum = zones_insert_zone(start, count, flags);
[98000fb]801 if (znum == (size_t) -1) {
[da1bafb]802 irq_spinlock_unlock(&zones.lock, true);
[98000fb]803 return (size_t) -1;
[5f0f29ce]804 }
[a35b458]805
[b0c2075]806 zone_construct(&zones.info[znum], start, count, flags, NULL);
[a35b458]807
[da1bafb]808 irq_spinlock_unlock(&zones.lock, true);
[a35b458]809
[bb68433]810 return znum;
[085d973]811}
812
[d1582b50]813/*
814 * Frame functions
815 */
[085d973]816
[deaf8d5]817/** Set parent of frame. */
[98000fb]818void frame_set_parent(pfn_t pfn, void *data, size_t hint)
[085d973]819{
[da1bafb]820 irq_spinlock_lock(&zones.lock, true);
[a35b458]821
[98000fb]822 size_t znum = find_zone(pfn, 1, hint);
[a35b458]823
[63e27ef]824 assert(znum != (size_t) -1);
[a35b458]825
[5f0f29ce]826 zone_get_frame(&zones.info[znum],
827 pfn - zones.info[znum].base)->parent = data;
[a35b458]828
[da1bafb]829 irq_spinlock_unlock(&zones.lock, true);
[085d973]830}
831
[98000fb]832void *frame_get_parent(pfn_t pfn, size_t hint)
[085d973]833{
[da1bafb]834 irq_spinlock_lock(&zones.lock, true);
[a35b458]835
[98000fb]836 size_t znum = find_zone(pfn, 1, hint);
[a35b458]837
[63e27ef]838 assert(znum != (size_t) -1);
[a35b458]839
[5f0f29ce]840 void *res = zone_get_frame(&zones.info[znum],
841 pfn - zones.info[znum].base)->parent;
[a35b458]842
[da1bafb]843 irq_spinlock_unlock(&zones.lock, true);
[a35b458]844
[085d973]845 return res;
846}
847
[482f968]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
[b0c2075]862/** Allocate frames of physical memory.
[085d973]863 *
[b0c2075]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.
[085d973]869 *
[5f0f29ce]870 * @return Physical address of the allocated frame.
[9a68b34d]871 *
[085d973]872 */
[b0c2075]873uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
[8cbf1c3]874 uintptr_t constraint, size_t *pzone)
[085d973]875{
[63e27ef]876 assert(count > 0);
[a35b458]877
[98000fb]878 size_t hint = pzone ? (*pzone) : 0;
[b0c2075]879 pfn_t frame_constraint = ADDR2PFN(constraint);
[a35b458]880
[89bcb520]881 /*
882 * If not told otherwise, we must first reserve the memory.
883 */
[b0c2075]884 if (!(flags & FRAME_NO_RESERVE))
885 reserve_force_alloc(count);
[a35b458]886
[085d973]887loop:
[da1bafb]888 irq_spinlock_lock(&zones.lock, true);
[a35b458]889
[482f968]890 // TODO: Print diagnostic if neither is explicitly specified.
891 bool lowmem = (flags & FRAME_LOWMEM) || !(flags & FRAME_HIGHMEM);
892
[085d973]893 /*
894 * First, find suitable frame zone.
895 */
[482f968]896 size_t znum = try_find_zone(count, lowmem, frame_constraint, hint);
[a35b458]897
[b0c2075]898 /*
899 * If no memory, reclaim some slab memory,
900 * if it does not help, reclaim all.
901 */
[98000fb]902 if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
[da1bafb]903 irq_spinlock_unlock(&zones.lock, true);
[98000fb]904 size_t freed = slab_reclaim(0);
[da1bafb]905 irq_spinlock_lock(&zones.lock, true);
[a35b458]906
[5f0f29ce]907 if (freed > 0)
[482f968]908 znum = try_find_zone(count, lowmem,
[b0c2075]909 frame_constraint, hint);
[a35b458]910
[98000fb]911 if (znum == (size_t) -1) {
[da1bafb]912 irq_spinlock_unlock(&zones.lock, true);
[085d973]913 freed = slab_reclaim(SLAB_RECLAIM_ALL);
[da1bafb]914 irq_spinlock_lock(&zones.lock, true);
[a35b458]915
[5f0f29ce]916 if (freed > 0)
[482f968]917 znum = try_find_zone(count, lowmem,
[b0c2075]918 frame_constraint, hint);
[085d973]919 }
920 }
[a35b458]921
[98000fb]922 if (znum == (size_t) -1) {
[1a1744e]923 if (flags & FRAME_ATOMIC) {
[da1bafb]924 irq_spinlock_unlock(&zones.lock, true);
[a35b458]925
[89bcb520]926 if (!(flags & FRAME_NO_RESERVE))
[b0c2075]927 reserve_free(count);
[a35b458]928
[8cbf1c3]929 return 0;
[1a1744e]930 }
[a35b458]931
[8d308b9]932 size_t avail = frame_total_free_get_internal();
[a35b458]933
[da1bafb]934 irq_spinlock_unlock(&zones.lock, true);
[a35b458]935
[05411e8]936 if (!THREAD)
[adb252c0]937 panic("Cannot wait for %zu frames to become available "
938 "(%zu available).", count, avail);
[a35b458]939
[5f0f29ce]940 /*
941 * Sleep until some frames are available again.
942 */
[a35b458]943
[5f0f29ce]944#ifdef CONFIG_DEBUG
[b2fa1204]945 log(LF_OTHER, LVL_DEBUG,
946 "Thread %" PRIu64 " waiting for %zu frames "
947 "%zu available.", THREAD->tid, count, avail);
[5f0f29ce]948#endif
[a35b458]949
[905721b]950 /*
[b0c2075]951 * Since the mem_avail_mtx is an active mutex, we need to
952 * disable interrupts to prevent deadlock with TLB shootdown.
[905721b]953 */
954 ipl_t ipl = interrupts_disable();
[1bb3766]955 mutex_lock(&mem_avail_mtx);
[a35b458]956
[5f0f29ce]957 if (mem_avail_req > 0)
[b0c2075]958 mem_avail_req = min(mem_avail_req, count);
[5f0f29ce]959 else
[b0c2075]960 mem_avail_req = count;
[a35b458]961
[98000fb]962 size_t gen = mem_avail_gen;
[a35b458]963
[5f0f29ce]964 while (gen == mem_avail_gen)
[1bb3766]965 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
[a35b458]966
[1bb3766]967 mutex_unlock(&mem_avail_mtx);
[905721b]968 interrupts_restore(ipl);
[a35b458]969
[1a1744e]970#ifdef CONFIG_DEBUG
[b2fa1204]971 log(LF_OTHER, LVL_DEBUG, "Thread %" PRIu64 " woken up.",
972 THREAD->tid);
[1a1744e]973#endif
[a35b458]974
[085d973]975 goto loop;
976 }
[a35b458]977
[b0c2075]978 pfn_t pfn = zone_frame_alloc(&zones.info[znum], count,
979 frame_constraint) + zones.info[znum].base;
[a35b458]980
[da1bafb]981 irq_spinlock_unlock(&zones.lock, true);
[a35b458]982
[5f0f29ce]983 if (pzone)
984 *pzone = znum;
[a35b458]985
[8cbf1c3]986 return PFN2ADDR(pfn);
[085d973]987}
988
[b0c2075]989uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
[0139747]990{
[b0c2075]991 return frame_alloc_generic(count, flags, constraint, NULL);
[0139747]992}
993
[5df1963]994/** Free frames of physical memory.
[085d973]995 *
[5df1963]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.
[5f0f29ce]999 *
[5df1963]1000 * @param start Physical Address of the first frame to be freed.
1001 * @param count Number of frames to free.
[c32e6bc]1002 * @param flags Flags to control memory reservation.
[085d973]1003 *
1004 */
[5df1963]1005void frame_free_generic(uintptr_t start, size_t count, frame_flags_t flags)
[085d973]1006{
[5df1963]1007 size_t freed = 0;
[a35b458]1008
[5df1963]1009 irq_spinlock_lock(&zones.lock, true);
[a35b458]1010
[5df1963]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);
[a35b458]1017
[63e27ef]1018 assert(znum != (size_t) -1);
[a35b458]1019
[5df1963]1020 freed += zone_frame_free(&zones.info[znum],
1021 pfn - zones.info[znum].base);
1022 }
[a35b458]1023
[da1bafb]1024 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1025
[1a1744e]1026 /*
1027 * Signal that some memory has been freed.
[5df1963]1028 * Since the mem_avail_mtx is an active mutex,
1029 * we need to disable interruptsto prevent deadlock
1030 * with TLB shootdown.
[905721b]1031 */
[a35b458]1032
[905721b]1033 ipl_t ipl = interrupts_disable();
[1bb3766]1034 mutex_lock(&mem_avail_mtx);
[a35b458]1035
[5f0f29ce]1036 if (mem_avail_req > 0)
[5df1963]1037 mem_avail_req -= min(mem_avail_req, freed);
[a35b458]1038
[5f0f29ce]1039 if (mem_avail_req == 0) {
1040 mem_avail_gen++;
1041 condvar_broadcast(&mem_avail_cv);
1042 }
[a35b458]1043
[1bb3766]1044 mutex_unlock(&mem_avail_mtx);
[905721b]1045 interrupts_restore(ipl);
[a35b458]1046
[89bcb520]1047 if (!(flags & FRAME_NO_RESERVE))
[5df1963]1048 reserve_free(freed);
[085d973]1049}
1050
[5df1963]1051void frame_free(uintptr_t frame, size_t count)
[c32e6bc]1052{
[5df1963]1053 frame_free_generic(frame, count, 0);
[c32e6bc]1054}
1055
[5df1963]1056void frame_free_noreserve(uintptr_t frame, size_t count)
[e608cbe]1057{
[5df1963]1058 frame_free_generic(frame, count, FRAME_NO_RESERVE);
[e608cbe]1059}
1060
[f3ac636]1061/** Add reference to frame.
1062 *
1063 * Find respective frame structure for supplied PFN and
1064 * increment frame reference count.
1065 *
[5f0f29ce]1066 * @param pfn Frame number of the frame to be freed.
1067 *
[f3ac636]1068 */
[8df5f20]1069_NO_TRACE void frame_reference_add(pfn_t pfn)
[f3ac636]1070{
[da1bafb]1071 irq_spinlock_lock(&zones.lock, true);
[a35b458]1072
[f3ac636]1073 /*
1074 * First, find host frame zone for addr.
1075 */
[0b4a67a]1076 size_t znum = find_zone(pfn, 1, 0);
[a35b458]1077
[63e27ef]1078 assert(znum != (size_t) -1);
[a35b458]1079
[5f0f29ce]1080 zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
[a35b458]1081
[da1bafb]1082 irq_spinlock_unlock(&zones.lock, true);
[f3ac636]1083}
[085d973]1084
[da1bafb]1085/** Mark given range unavailable in frame zones.
1086 *
1087 */
[8df5f20]1088_NO_TRACE void frame_mark_unavailable(pfn_t start, size_t count)
[085d973]1089{
[da1bafb]1090 irq_spinlock_lock(&zones.lock, true);
[a35b458]1091
[b0c2075]1092 for (size_t i = 0; i < count; i++) {
[98000fb]1093 size_t znum = find_zone(start + i, 1, 0);
[a35b458]1094
[98000fb]1095 if (znum == (size_t) -1) /* PFN not found */
[085d973]1096 continue;
[a35b458]1097
[5f0f29ce]1098 zone_mark_unavailable(&zones.info[znum],
1099 start + i - zones.info[znum].base);
[085d973]1100 }
[a35b458]1101
[da1bafb]1102 irq_spinlock_unlock(&zones.lock, true);
[085d973]1103}
1104
[da1bafb]1105/** Initialize physical memory management.
1106 *
1107 */
[085d973]1108void frame_init(void)
1109{
1110 if (config.cpu_active == 1) {
1111 zones.count = 0;
[da1bafb]1112 irq_spinlock_initialize(&zones.lock, "frame.zones.lock");
[1bb3766]1113 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1114 condvar_initialize(&mem_avail_cv);
[085d973]1115 }
[a35b458]1116
[085d973]1117 /* Tell the architecture to create some memory */
[ddcc8a0]1118 frame_low_arch_init();
[a35b458]1119
[085d973]1120 if (config.cpu_active == 1) {
[4638401]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));
[a35b458]1125
[b0c2075]1126 for (size_t i = 0; i < init.cnt; i++)
1127 frame_mark_unavailable(ADDR2PFN(init.tasks[i].paddr),
[4638401]1128 SIZE2FRAMES(init.tasks[i].size));
[a35b458]1129
[61e90dd]1130 if (ballocs.size)
[4638401]1131 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1132 SIZE2FRAMES(ballocs.size));
[a35b458]1133
[b0c2075]1134 /*
1135 * Blacklist first frame, as allocating NULL would
[5f0f29ce]1136 * fail in some places
1137 */
[bffa0b06]1138 frame_mark_unavailable(0, 1);
[085d973]1139 }
[a35b458]1140
[ddcc8a0]1141 frame_high_arch_init();
[085d973]1142}
1143
[ad12b5ea]1144/** Adjust bounds of physical memory region according to low/high memory split.
1145 *
[b0c2075]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 *
[ad12b5ea]1157 */
1158bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
1159{
[2cc7f16]1160 uintptr_t limit = KA2PA(config.identity_base) + config.identity_size;
[a35b458]1161
[ad12b5ea]1162 if (low) {
1163 if (*basep > limit)
1164 return false;
[a35b458]1165
[ad12b5ea]1166 if (*basep + *sizep > limit)
1167 *sizep = limit - *basep;
1168 } else {
1169 if (*basep + *sizep <= limit)
1170 return false;
[a35b458]1171
[ad12b5ea]1172 if (*basep <= limit) {
1173 *sizep -= limit - *basep;
1174 *basep = limit;
1175 }
1176 }
[a35b458]1177
[ad12b5ea]1178 return true;
1179}
1180
[da1bafb]1181/** Return total size of all zones.
1182 *
1183 */
[9dae191e]1184uint64_t zones_total_size(void)
[deaf8d5]1185{
[da1bafb]1186 irq_spinlock_lock(&zones.lock, true);
[a35b458]1187
[5f0f29ce]1188 uint64_t total = 0;
[a35b458]1189
[b0c2075]1190 for (size_t i = 0; i < zones.count; i++)
[5f0f29ce]1191 total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
[a35b458]1192
[da1bafb]1193 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1194
[71eef11]1195 return total;
1196}
1197
[9dae191e]1198void zones_stats(uint64_t *total, uint64_t *unavail, uint64_t *busy,
1199 uint64_t *free)
[516adce]1200{
[63e27ef]1201 assert(total != NULL);
1202 assert(unavail != NULL);
1203 assert(busy != NULL);
1204 assert(free != NULL);
[a35b458]1205
[da1bafb]1206 irq_spinlock_lock(&zones.lock, true);
[a35b458]1207
[9dae191e]1208 *total = 0;
1209 *unavail = 0;
1210 *busy = 0;
1211 *free = 0;
[a35b458]1212
[b0c2075]1213 for (size_t i = 0; i < zones.count; i++) {
[9dae191e]1214 *total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
[a35b458]1215
[e6c4b94]1216 if (zones.info[i].flags & ZONE_AVAILABLE) {
[9dae191e]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);
[516adce]1221 }
[a35b458]1222
[da1bafb]1223 irq_spinlock_unlock(&zones.lock, true);
[516adce]1224}
1225
[da1bafb]1226/** Prints list of zones.
1227 *
1228 */
[9dae191e]1229void zones_print_list(void)
[deaf8d5]1230{
[5f0f29ce]1231#ifdef __32_BITS__
[74c5a1ca]1232 printf("[nr] [base addr] [frames ] [flags ] [free frames ] [busy frames ]\n");
[2b8b0ca]1233#endif
1234
1235#ifdef __64_BITS__
[74c5a1ca]1236 printf("[nr] [base address ] [frames ] [flags ] [free frames ] [busy frames ]\n");
[2b8b0ca]1237#endif
[a35b458]1238
[000350f8]1239 /*
1240 * Because printing may require allocation of memory, we may not hold
[f72906c]1241 * the frame allocator locks when printing zone statistics. Therefore,
[000350f8]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 */
[a35b458]1249
[f72906c]1250 size_t free_lowmem = 0;
1251 size_t free_highmem = 0;
1252 size_t free_highprio = 0;
[a35b458]1253
[3bacee1]1254 for (size_t i = 0; ; i++) {
[da1bafb]1255 irq_spinlock_lock(&zones.lock, true);
[a35b458]1256
[000350f8]1257 if (i >= zones.count) {
[da1bafb]1258 irq_spinlock_unlock(&zones.lock, true);
[000350f8]1259 break;
1260 }
[a35b458]1261
[f72906c]1262 pfn_t fbase = zones.info[i].base;
1263 uintptr_t base = PFN2ADDR(fbase);
[98000fb]1264 size_t count = zones.info[i].count;
[5f0f29ce]1265 zone_flags_t flags = zones.info[i].flags;
[98000fb]1266 size_t free_count = zones.info[i].free_count;
1267 size_t busy_count = zones.info[i].busy_count;
[a35b458]1268
[e6c4b94]1269 bool available = ((flags & ZONE_AVAILABLE) != 0);
[f72906c]1270 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1271 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1272 bool highprio = is_high_priority(fbase, count);
[a35b458]1273
[f72906c]1274 if (available) {
1275 if (lowmem)
1276 free_lowmem += free_count;
[a35b458]1277
[f72906c]1278 if (highmem)
1279 free_highmem += free_count;
[a35b458]1280
[f72906c]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 */
[a35b458]1289
[f72906c]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 }
[a35b458]1299
[f72906c]1300 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1301
[7e752b2]1302 printf("%-4zu", i);
[a35b458]1303
[2b8b0ca]1304#ifdef __32_BITS__
[7e752b2]1305 printf(" %p", (void *) base);
[2b8b0ca]1306#endif
[a35b458]1307
[2b8b0ca]1308#ifdef __64_BITS__
[7e752b2]1309 printf(" %p", (void *) base);
[e49e234]1310#endif
[a35b458]1311
[e6c4b94]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' : '-');
[a35b458]1318
[5f0f29ce]1319 if (available)
[7e752b2]1320 printf("%14zu %14zu",
[5f0f29ce]1321 free_count, busy_count);
[a35b458]1322
[5f0f29ce]1323 printf("\n");
[dfd9186]1324 }
[a35b458]1325
[f72906c]1326 printf("\n");
[a35b458]1327
[f72906c]1328 uint64_t size;
1329 const char *size_suffix;
[a35b458]1330
[f72906c]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);
[a35b458]1335
[f72906c]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);
[a35b458]1340
[f72906c]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);
[dfd9186]1345}
1346
[abbc16e]1347/** Prints zone details.
[96cacc1]1348 *
[5f0f29ce]1349 * @param num Zone base address or zone number.
1350 *
[96cacc1]1351 */
[98000fb]1352void zone_print_one(size_t num)
[deaf8d5]1353{
[da1bafb]1354 irq_spinlock_lock(&zones.lock, true);
[98000fb]1355 size_t znum = (size_t) -1;
[a35b458]1356
[b0c2075]1357 for (size_t i = 0; i < zones.count; i++) {
[5f0f29ce]1358 if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
1359 znum = i;
[bb68433]1360 break;
1361 }
1362 }
[a35b458]1363
[98000fb]1364 if (znum == (size_t) -1) {
[da1bafb]1365 irq_spinlock_unlock(&zones.lock, true);
[bb68433]1366 printf("Zone not found.\n");
[2ec725f]1367 return;
[dfd9186]1368 }
[a35b458]1369
[f72906c]1370 size_t free_lowmem = 0;
1371 size_t free_highmem = 0;
1372 size_t free_highprio = 0;
[a35b458]1373
[f72906c]1374 pfn_t fbase = zones.info[znum].base;
1375 uintptr_t base = PFN2ADDR(fbase);
[b0c2075]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;
[a35b458]1380
[e6c4b94]1381 bool available = ((flags & ZONE_AVAILABLE) != 0);
[f72906c]1382 bool lowmem = ((flags & ZONE_LOWMEM) != 0);
1383 bool highmem = ((flags & ZONE_HIGHMEM) != 0);
1384 bool highprio = is_high_priority(fbase, count);
[a35b458]1385
[f72906c]1386 if (available) {
1387 if (lowmem)
1388 free_lowmem = free_count;
[a35b458]1389
[f72906c]1390 if (highmem)
1391 free_highmem = free_count;
[a35b458]1392
[f72906c]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 */
[a35b458]1401
[f72906c]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 }
[a35b458]1411
[f72906c]1412 irq_spinlock_unlock(&zones.lock, true);
[a35b458]1413
[933cadf]1414 uint64_t size;
1415 const char *size_suffix;
[a35b458]1416
[933cadf]1417 bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
[a35b458]1418
[f72906c]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,
[933cadf]1422 size, size_suffix);
[f72906c]1423 printf("Zone flags: %c%c%c%c%c\n",
[e6c4b94]1424 available ? 'A' : '-',
1425 (flags & ZONE_RESERVED) ? 'R' : '-',
1426 (flags & ZONE_FIRMWARE) ? 'F' : '-',
1427 (flags & ZONE_LOWMEM) ? 'L' : '-',
1428 (flags & ZONE_HIGHMEM) ? 'H' : '-');
[a35b458]1429
[5f0f29ce]1430 if (available) {
[933cadf]1431 bin_order_suffix(FRAMES2SIZE(busy_count), &size, &size_suffix,
1432 false);
[f72906c]1433 printf("Allocated space: %zu frames (%" PRIu64 " %s)\n",
[933cadf]1434 busy_count, size, size_suffix);
[a35b458]1435
[933cadf]1436 bin_order_suffix(FRAMES2SIZE(free_count), &size, &size_suffix,
1437 false);
[f72906c]1438 printf("Available space: %zu frames (%" PRIu64 " %s)\n",
[933cadf]1439 free_count, size, size_suffix);
[a35b458]1440
[f72906c]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);
[a35b458]1445
[f72906c]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);
[a35b458]1450
[f72906c]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);
[5f0f29ce]1455 }
[dfd9186]1456}
1457
[cc73a8a1]1458/** @}
[b45c443]1459 */
Note: See TracBrowser for help on using the repository browser.