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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 314f4b59 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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