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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0b4a67a was 0b4a67a, checked in by Jakub Jermar <jakub@…>, 15 years ago

Use a more portable definition of NULL.

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