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

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

use binary suffixes in printouts where appropriate

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