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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 933cadf 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
Line 
1/*
2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
4 * Copyright (c) 2009 Martin Decky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup genericmm
32 * @{
33 */
34
35/**
36 * @file
37 * @brief Physical frame allocator.
38 *
39 * This file contains the physical frame allocator and memory zone management.
40 * The frame allocator is built on top of the buddy allocator.
41 *
42 * @see buddy.c
43 */
44
45#include <typedefs.h>
46#include <mm/frame.h>
47#include <mm/reserve.h>
48#include <mm/as.h>
49#include <panic.h>
50#include <debug.h>
51#include <adt/list.h>
52#include <synch/mutex.h>
53#include <synch/condvar.h>
54#include <arch/asm.h>
55#include <arch.h>
56#include <print.h>
57#include <align.h>
58#include <mm/slab.h>
59#include <bitops.h>
60#include <macros.h>
61#include <config.h>
62#include <str.h>
63
64zones_t zones;
65
66/*
67 * Synchronization primitives used to sleep when there is no memory
68 * available.
69 */
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. */
74
75/********************/
76/* Helper functions */
77/********************/
78
79NO_TRACE static inline size_t frame_index(zone_t *zone, frame_t *frame)
80{
81 return (size_t) (frame - zone->frames);
82}
83
84NO_TRACE static inline size_t frame_index_abs(zone_t *zone, frame_t *frame)
85{
86 return (size_t) (frame - zone->frames) + zone->base;
87}
88
89NO_TRACE static inline bool frame_index_valid(zone_t *zone, size_t index)
90{
91 return (index < zone->count);
92}
93
94NO_TRACE static inline size_t make_frame_index(zone_t *zone, frame_t *frame)
95{
96 return (frame - zone->frames);
97}
98
99/** Initialize frame structure.
100 *
101 * @param frame Frame structure to be initialized.
102 *
103 */
104NO_TRACE static void frame_initialize(frame_t *frame)
105{
106 frame->refcount = 1;
107 frame->buddy_order = 0;
108}
109
110/*******************/
111/* Zones functions */
112/*******************/
113
114/** Insert-sort zone into zones list.
115 *
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 *
124 */
125NO_TRACE static size_t zones_insert_zone(pfn_t base, size_t count,
126 zone_flags_t flags)
127{
128 if (zones.count + 1 == ZONES_MAX) {
129 printf("Maximum zone count %u exceeded!\n", ZONES_MAX);
130 return (size_t) -1;
131 }
132
133 size_t i;
134 for (i = 0; i < zones.count; i++) {
135 /* Check for overlap */
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))) {
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));
154 }
155
156 return (size_t) -1;
157 }
158 if (base < zones.info[i].base)
159 break;
160 }
161
162 /* Move other zones up */
163 size_t j;
164 for (j = zones.count; j > i; j--) {
165 zones.info[j] = zones.info[j - 1];
166 if (zones.info[j].buddy_system != NULL)
167 zones.info[j].buddy_system->data =
168 (void *) &zones.info[j];
169 }
170
171 zones.count++;
172
173 return i;
174}
175
176/** Get total available frames.
177 *
178 * Assume interrupts are disabled and zones lock is
179 * locked.
180 *
181 * @return Total number of available frames.
182 *
183 */
184#ifdef CONFIG_DEBUG
185NO_TRACE static size_t total_frames_free(void)
186{
187 size_t total = 0;
188 size_t i;
189 for (i = 0; i < zones.count; i++)
190 total += zones.info[i].free_count;
191
192 return total;
193}
194#endif /* CONFIG_DEBUG */
195
196/** Find a zone with a given frames.
197 *
198 * Assume interrupts are disabled and zones lock is
199 * locked.
200 *
201 * @param frame Frame number contained in zone.
202 * @param count Number of frames to look for.
203 * @param hint Used as zone hint.
204 *
205 * @return Zone index or -1 if not found.
206 *
207 */
208NO_TRACE size_t find_zone(pfn_t frame, size_t count, size_t hint)
209{
210 if (hint >= zones.count)
211 hint = 0;
212
213 size_t i = hint;
214 do {
215 if ((zones.info[i].base <= frame)
216 && (zones.info[i].base + zones.info[i].count >= frame + count))
217 return i;
218
219 i++;
220 if (i >= zones.count)
221 i = 0;
222
223 } while (i != hint);
224
225 return (size_t) -1;
226}
227
228/** @return True if zone can allocate specified order */
229NO_TRACE static bool zone_can_alloc(zone_t *zone, uint8_t order)
230{
231 return (zone_flags_available(zone->flags)
232 && buddy_system_can_alloc(zone->buddy_system, order));
233}
234
235/** Find a zone that can allocate order frames.
236 *
237 * Assume interrupts are disabled and zones lock is
238 * locked.
239 *
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.
243 *
244 */
245NO_TRACE static size_t find_free_zone(uint8_t order, zone_flags_t flags,
246 size_t hint)
247{
248 if (hint >= zones.count)
249 hint = 0;
250
251 size_t i = hint;
252 do {
253 /*
254 * Check whether the zone meets the search criteria.
255 */
256 if ((zones.info[i].flags & flags) == flags) {
257 /*
258 * Check if the zone has 2^order frames area available.
259 */
260 if (zone_can_alloc(&zones.info[i], order))
261 return i;
262 }
263
264 i++;
265 if (i >= zones.count)
266 i = 0;
267
268 } while (i != hint);
269
270 return (size_t) -1;
271}
272
273/**************************/
274/* Buddy system functions */
275/**************************/
276
277/** Buddy system find_block implementation.
278 *
279 * Find block that is parent of current list.
280 * That means go to lower addresses, until such block is found
281 *
282 * @param order Order of parent must be different then this
283 * parameter!!
284 *
285 */
286NO_TRACE static link_t *zone_buddy_find_block(buddy_system_t *buddy,
287 link_t *child, uint8_t order)
288{
289 frame_t *frame = list_get_instance(child, frame_t, buddy_link);
290 zone_t *zone = (zone_t *) buddy->data;
291
292 size_t index = frame_index(zone, frame);
293 do {
294 if (zone->frames[index].buddy_order != order)
295 return &zone->frames[index].buddy_link;
296 } while (index-- > 0);
297
298 return NULL;
299}
300
301/** Buddy system find_buddy implementation.
302 *
303 * @param buddy Buddy system.
304 * @param block Block for which buddy should be found.
305 *
306 * @return Buddy for given block if found.
307 *
308 */
309NO_TRACE static link_t *zone_buddy_find_buddy(buddy_system_t *buddy,
310 link_t *block)
311{
312 frame_t *frame = list_get_instance(block, frame_t, buddy_link);
313 zone_t *zone = (zone_t *) buddy->data;
314 ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),
315 frame->buddy_order));
316
317 bool is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
318
319 size_t index;
320 if (is_left) {
321 index = (frame_index(zone, frame)) +
322 (1 << frame->buddy_order);
323 } else { /* is_right */
324 index = (frame_index(zone, frame)) -
325 (1 << frame->buddy_order);
326 }
327
328 if (frame_index_valid(zone, index)) {
329 if ((zone->frames[index].buddy_order == frame->buddy_order) &&
330 (zone->frames[index].refcount == 0)) {
331 return &zone->frames[index].buddy_link;
332 }
333 }
334
335 return NULL;
336}
337
338/** Buddy system bisect implementation.
339 *
340 * @param buddy Buddy system.
341 * @param block Block to bisect.
342 *
343 * @return Right block.
344 *
345 */
346NO_TRACE static link_t *zone_buddy_bisect(buddy_system_t *buddy, link_t *block)
347{
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)));
350
351 return &frame_r->buddy_link;
352}
353
354/** Buddy system coalesce implementation.
355 *
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).
362 *
363 */
364NO_TRACE static link_t *zone_buddy_coalesce(buddy_system_t *buddy,
365 link_t *block_1, link_t *block_2)
366{
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);
369
370 return ((frame1 < frame2) ? block_1 : block_2);
371}
372
373/** Buddy system set_order implementation.
374 *
375 * @param buddy Buddy system.
376 * @param block Buddy system block.
377 * @param order Order to set.
378 *
379 */
380NO_TRACE static void zone_buddy_set_order(buddy_system_t *buddy, link_t *block,
381 uint8_t order)
382{
383 list_get_instance(block, frame_t, buddy_link)->buddy_order = order;
384}
385
386/** Buddy system get_order implementation.
387 *
388 * @param buddy Buddy system.
389 * @param block Buddy system block.
390 *
391 * @return Order of block.
392 *
393 */
394NO_TRACE static uint8_t zone_buddy_get_order(buddy_system_t *buddy,
395 link_t *block)
396{
397 return list_get_instance(block, frame_t, buddy_link)->buddy_order;
398}
399
400/** Buddy system mark_busy implementation.
401 *
402 * @param buddy Buddy system.
403 * @param block Buddy system block.
404 *
405 */
406NO_TRACE static void zone_buddy_mark_busy(buddy_system_t *buddy, link_t *block)
407{
408 list_get_instance(block, frame_t, buddy_link)->refcount = 1;
409}
410
411/** Buddy system mark_available implementation.
412 *
413 * @param buddy Buddy system.
414 * @param block Buddy system block.
415 *
416 */
417NO_TRACE static void zone_buddy_mark_available(buddy_system_t *buddy,
418 link_t *block)
419{
420 list_get_instance(block, frame_t, buddy_link)->refcount = 0;
421}
422
423static buddy_system_operations_t zone_buddy_system_operations = {
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,
431 .find_block = zone_buddy_find_block
432};
433
434/******************/
435/* Zone functions */
436/******************/
437
438/** Allocate frame in particular zone.
439 *
440 * Assume zone is locked and is available for allocation.
441 * Panics if allocation is impossible.
442 *
443 * @param zone Zone to allocate from.
444 * @param order Allocate exactly 2^order frames.
445 *
446 * @return Frame index in zone.
447 *
448 */
449NO_TRACE static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
450{
451 ASSERT(zone_flags_available(zone->flags));
452
453 /* Allocate frames from zone buddy system */
454 link_t *link = buddy_system_alloc(zone->buddy_system, order);
455
456 ASSERT(link);
457
458 /* Update zone information. */
459 zone->free_count -= (1 << order);
460 zone->busy_count += (1 << order);
461
462 /* Frame will be actually a first frame of the block. */
463 frame_t *frame = list_get_instance(link, frame_t, buddy_link);
464
465 /* Get frame address */
466 return make_frame_index(zone, frame);
467}
468
469/** Free frame from zone.
470 *
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.
475 *
476 * @return Number of freed frames.
477 *
478 */
479NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t frame_idx)
480{
481 ASSERT(zone_flags_available(zone->flags));
482
483 frame_t *frame = &zone->frames[frame_idx];
484 size_t size = 0;
485
486 ASSERT(frame->refcount);
487
488 if (!--frame->refcount) {
489 size = 1 << frame->buddy_order;
490 buddy_system_free(zone->buddy_system, &frame->buddy_link);
491 /* Update zone information. */
492 zone->free_count += size;
493 zone->busy_count -= size;
494 }
495
496 return size;
497}
498
499/** Return frame from zone. */
500NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t frame_idx)
501{
502 ASSERT(frame_idx < zone->count);
503 return &zone->frames[frame_idx];
504}
505
506/** Mark frame in zone unavailable to allocation. */
507NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t frame_idx)
508{
509 ASSERT(zone_flags_available(zone->flags));
510
511 frame_t *frame = zone_get_frame(zone, frame_idx);
512 if (frame->refcount)
513 return;
514
515 link_t *link __attribute__ ((unused));
516
517 link = buddy_system_alloc_block(zone->buddy_system,
518 &frame->buddy_link);
519
520 ASSERT(link);
521 zone->free_count--;
522 reserve_force_alloc(1);
523}
524
525/** Merge two zones.
526 *
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.
530 *
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.
535 *
536 */
537NO_TRACE static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1,
538 buddy_system_t *buddy)
539{
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 */
564 size_t i;
565 for (i = 0; i < zones.info[z1].count; i++)
566 frame_initialize(&zones.info[z1].frames[i]);
567
568 /* Copy frames from both zones to preserve full frame orders,
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
572 * will not be reallocated during merge and it would make later
573 * problems with allocation/free.
574 */
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
582 i = 0;
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;
594 i++;
595 }
596 }
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];
605 frame->refcount = 0;
606 buddy_system_free(zones.info[z1].buddy_system, &frame->buddy_link);
607 }
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];
616 frame->refcount = 0;
617 buddy_system_free(zones.info[z1].buddy_system, &frame->buddy_link);
618 }
619}
620
621/** Return old configuration frames into the zone.
622 *
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.
632 *
633 */
634NO_TRACE static void return_config_frames(size_t znum, pfn_t pfn, size_t count)
635{
636 ASSERT(zone_flags_available(zones.info[znum].flags));
637
638 size_t cframes = SIZE2FRAMES(zone_conf_size(count));
639
640 if ((pfn < zones.info[znum].base)
641 || (pfn >= zones.info[znum].base + zones.info[znum].count))
642 return;
643
644 frame_t *frame __attribute__ ((unused));
645
646 frame = &zones.info[znum].frames[pfn - zones.info[znum].base];
647 ASSERT(!frame->buddy_order);
648
649 size_t i;
650 for (i = 0; i < cframes; i++) {
651 zones.info[znum].busy_count++;
652 (void) zone_frame_free(&zones.info[znum],
653 pfn - zones.info[znum].base + i);
654 }
655}
656
657/** Reduce allocated block to count of order 0 frames.
658 *
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,
662 * you have to free every frame.
663 *
664 * @param znum Zone.
665 * @param frame_idx Index the first frame of the block.
666 * @param count Allocated frames in block.
667 *
668 */
669NO_TRACE static void zone_reduce_region(size_t znum, pfn_t frame_idx,
670 size_t count)
671{
672 ASSERT(zone_flags_available(zones.info[znum].flags));
673 ASSERT(frame_idx + count < zones.info[znum].count);
674
675 uint8_t order = zones.info[znum].frames[frame_idx].buddy_order;
676 ASSERT((size_t) (1 << order) >= count);
677
678 /* Reduce all blocks to order 0 */
679 size_t i;
680 for (i = 0; i < (size_t) (1 << order); i++) {
681 frame_t *frame = &zones.info[znum].frames[i + frame_idx];
682 frame->buddy_order = 0;
683 if (!frame->refcount)
684 frame->refcount = 1;
685 ASSERT(frame->refcount == 1);
686 }
687
688 /* Free unneeded frames */
689 for (i = count; i < (size_t) (1 << order); i++)
690 (void) zone_frame_free(&zones.info[znum], i + frame_idx);
691}
692
693/** Merge zones z1 and z2.
694 *
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 *
703 */
704bool zone_merge(size_t z1, size_t z2)
705{
706 irq_spinlock_lock(&zones.lock, true);
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;
720 goto errout;
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;
728 if (cframes == 1)
729 order = 0;
730 else
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
752 /* Free unneeded config frames */
753 zone_reduce_region(z1, pfn - zones.info[z1].base, cframes);
754
755 /* Subtract zone information from busy frames */
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
765 /* Move zones down */
766 size_t i;
767 for (i = z2 + 1; i < zones.count; i++) {
768 zones.info[i - 1] = zones.info[i];
769 if (zones.info[i - 1].buddy_system != NULL)
770 zones.info[i - 1].buddy_system->data =
771 (void *) &zones.info[i - 1];
772 }
773
774 zones.count--;
775
776errout:
777 irq_spinlock_unlock(&zones.lock, true);
778
779 return ret;
780}
781
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).
787 *
788 */
789void zone_merge_all(void)
790{
791 size_t i = 0;
792 while (i < zones.count) {
793 if (!zone_merge(i, i + 1))
794 i++;
795 }
796}
797
798/** Create new frame zone.
799 *
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.
807 *
808 */
809NO_TRACE static void zone_construct(zone_t *zone, buddy_system_t *buddy,
810 pfn_t start, size_t count, zone_flags_t flags)
811{
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;
818
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
833 size_t i;
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 }
842
843 /* "Unreserve" new frames. */
844 reserve_free(count);
845 } else
846 zone->frames = NULL;
847}
848
849/** Compute configuration data size for zone.
850 *
851 * @param count Size of zone in frames.
852 *
853 * @return Size of zone configuration info (in bytes).
854 *
855 */
856size_t zone_conf_size(size_t count)
857{
858 return (count * sizeof(frame_t) + buddy_conf_size(fnzb(count)));
859}
860
861/** Create and add zone to system.
862 *
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 *
876 */
877size_t zone_create(pfn_t start, size_t count, pfn_t confframe,
878 zone_flags_t flags)
879{
880 irq_spinlock_lock(&zones.lock, true);
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 */
887 ASSERT(confframe != ADDR2PFN((uintptr_t ) NULL));
888
889 /* If confframe is supposed to be inside our zone, then make sure
890 * it does not span kernel & init
891 */
892 size_t confcount = SIZE2FRAMES(zone_conf_size(count));
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
900 if (overlaps(addr, PFN2ADDR(confcount),
901 KA2PA(config.stack_base), config.stack_size))
902 continue;
903
904 bool overlap = false;
905 size_t i;
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 }
918
919 if (confframe >= start + count)
920 panic("Cannot find configuration data for zone.");
921 }
922
923 size_t znum = zones_insert_zone(start, count, flags);
924 if (znum == (size_t) -1) {
925 irq_spinlock_unlock(&zones.lock, true);
926 return (size_t) -1;
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)) {
934 size_t i;
935 for (i = confframe; i < confframe + confcount; i++)
936 zone_mark_unavailable(&zones.info[znum],
937 i - zones.info[znum].base);
938 }
939
940 irq_spinlock_unlock(&zones.lock, true);
941
942 return znum;
943 }
944
945 /* Non-available zone */
946 size_t znum = zones_insert_zone(start, count, flags);
947 if (znum == (size_t) -1) {
948 irq_spinlock_unlock(&zones.lock, true);
949 return (size_t) -1;
950 }
951 zone_construct(&zones.info[znum], NULL, start, count, flags);
952
953 irq_spinlock_unlock(&zones.lock, true);
954
955 return znum;
956}
957
958/*******************/
959/* Frame functions */
960/*******************/
961
962/** Set parent of frame. */
963void frame_set_parent(pfn_t pfn, void *data, size_t hint)
964{
965 irq_spinlock_lock(&zones.lock, true);
966
967 size_t znum = find_zone(pfn, 1, hint);
968
969 ASSERT(znum != (size_t) -1);
970
971 zone_get_frame(&zones.info[znum],
972 pfn - zones.info[znum].base)->parent = data;
973
974 irq_spinlock_unlock(&zones.lock, true);
975}
976
977void *frame_get_parent(pfn_t pfn, size_t hint)
978{
979 irq_spinlock_lock(&zones.lock, true);
980
981 size_t znum = find_zone(pfn, 1, hint);
982
983 ASSERT(znum != (size_t) -1);
984
985 void *res = zone_get_frame(&zones.info[znum],
986 pfn - zones.info[znum].base)->parent;
987
988 irq_spinlock_unlock(&zones.lock, true);
989
990 return res;
991}
992
993/** Allocate power-of-two frames of physical memory.
994 *
995 * @param order Allocate exactly 2^order frames.
996 * @param flags Flags for host zone selection and address processing.
997 * @param pzone Preferred zone.
998 *
999 * @return Physical address of the allocated frame.
1000 *
1001 */
1002void *frame_alloc_generic(uint8_t order, frame_flags_t flags, size_t *pzone)
1003{
1004 size_t size = ((size_t) 1) << order;
1005 size_t hint = pzone ? (*pzone) : 0;
1006
1007 /*
1008 * If not told otherwise, we must first reserve the memory.
1009 */
1010 if (!(flags & FRAME_NO_RESERVE))
1011 reserve_force_alloc(size);
1012
1013loop:
1014 irq_spinlock_lock(&zones.lock, true);
1015
1016 /*
1017 * First, find suitable frame zone.
1018 */
1019 size_t znum = find_free_zone(order,
1020 FRAME_TO_ZONE_FLAGS(flags), hint);
1021
1022 /* If no memory, reclaim some slab memory,
1023 if it does not help, reclaim all */
1024 if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
1025 irq_spinlock_unlock(&zones.lock, true);
1026 size_t freed = slab_reclaim(0);
1027 irq_spinlock_lock(&zones.lock, true);
1028
1029 if (freed > 0)
1030 znum = find_free_zone(order,
1031 FRAME_TO_ZONE_FLAGS(flags), hint);
1032
1033 if (znum == (size_t) -1) {
1034 irq_spinlock_unlock(&zones.lock, true);
1035 freed = slab_reclaim(SLAB_RECLAIM_ALL);
1036 irq_spinlock_lock(&zones.lock, true);
1037
1038 if (freed > 0)
1039 znum = find_free_zone(order,
1040 FRAME_TO_ZONE_FLAGS(flags), hint);
1041 }
1042 }
1043
1044 if (znum == (size_t) -1) {
1045 if (flags & FRAME_ATOMIC) {
1046 irq_spinlock_unlock(&zones.lock, true);
1047 if (!(flags & FRAME_NO_RESERVE))
1048 reserve_free(size);
1049 return NULL;
1050 }
1051
1052#ifdef CONFIG_DEBUG
1053 size_t avail = total_frames_free();
1054#endif
1055
1056 irq_spinlock_unlock(&zones.lock, true);
1057
1058 if (!THREAD)
1059 panic("Cannot wait for memory to become available.");
1060
1061 /*
1062 * Sleep until some frames are available again.
1063 */
1064
1065#ifdef CONFIG_DEBUG
1066 printf("Thread %" PRIu64 " waiting for %zu frames, "
1067 "%zu available.\n", THREAD->tid, size, avail);
1068#endif
1069
1070 mutex_lock(&mem_avail_mtx);
1071
1072 if (mem_avail_req > 0)
1073 mem_avail_req = min(mem_avail_req, size);
1074 else
1075 mem_avail_req = size;
1076 size_t gen = mem_avail_gen;
1077
1078 while (gen == mem_avail_gen)
1079 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
1080
1081 mutex_unlock(&mem_avail_mtx);
1082
1083#ifdef CONFIG_DEBUG
1084 printf("Thread %" PRIu64 " woken up.\n", THREAD->tid);
1085#endif
1086
1087 goto loop;
1088 }
1089
1090 pfn_t pfn = zone_frame_alloc(&zones.info[znum], order)
1091 + zones.info[znum].base;
1092
1093 irq_spinlock_unlock(&zones.lock, true);
1094
1095 if (pzone)
1096 *pzone = znum;
1097
1098 if (flags & FRAME_KA)
1099 return (void *) PA2KA(PFN2ADDR(pfn));
1100
1101 return (void *) PFN2ADDR(pfn);
1102}
1103
1104void *frame_alloc(uint8_t order, frame_flags_t flags)
1105{
1106 return frame_alloc_generic(order, flags, NULL);
1107}
1108
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
1114/** Free a frame.
1115 *
1116 * Find respective frame structure for supplied physical frame address.
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.
1121 * @param flags Flags to control memory reservation.
1122 *
1123 */
1124void frame_free_generic(uintptr_t frame, frame_flags_t flags)
1125{
1126 size_t size;
1127
1128 irq_spinlock_lock(&zones.lock, true);
1129
1130 /*
1131 * First, find host frame zone for addr.
1132 */
1133 pfn_t pfn = ADDR2PFN(frame);
1134 size_t znum = find_zone(pfn, 1, 0);
1135
1136
1137 ASSERT(znum != (size_t) -1);
1138
1139 size = zone_frame_free(&zones.info[znum], pfn - zones.info[znum].base);
1140
1141 irq_spinlock_unlock(&zones.lock, true);
1142
1143 /*
1144 * Signal that some memory has been freed.
1145 */
1146 mutex_lock(&mem_avail_mtx);
1147 if (mem_avail_req > 0)
1148 mem_avail_req -= min(mem_avail_req, size);
1149
1150 if (mem_avail_req == 0) {
1151 mem_avail_gen++;
1152 condvar_broadcast(&mem_avail_cv);
1153 }
1154 mutex_unlock(&mem_avail_mtx);
1155
1156 if (!(flags & FRAME_NO_RESERVE))
1157 reserve_free(size);
1158}
1159
1160void frame_free(uintptr_t frame)
1161{
1162 frame_free_generic(frame, 0);
1163}
1164
1165void frame_free_noreserve(uintptr_t frame)
1166{
1167 frame_free_generic(frame, FRAME_NO_RESERVE);
1168}
1169
1170/** Add reference to frame.
1171 *
1172 * Find respective frame structure for supplied PFN and
1173 * increment frame reference count.
1174 *
1175 * @param pfn Frame number of the frame to be freed.
1176 *
1177 */
1178NO_TRACE void frame_reference_add(pfn_t pfn)
1179{
1180 irq_spinlock_lock(&zones.lock, true);
1181
1182 /*
1183 * First, find host frame zone for addr.
1184 */
1185 size_t znum = find_zone(pfn, 1, 0);
1186
1187 ASSERT(znum != (size_t) -1);
1188
1189 zones.info[znum].frames[pfn - zones.info[znum].base].refcount++;
1190
1191 irq_spinlock_unlock(&zones.lock, true);
1192}
1193
1194/** Mark given range unavailable in frame zones.
1195 *
1196 */
1197NO_TRACE void frame_mark_unavailable(pfn_t start, size_t count)
1198{
1199 irq_spinlock_lock(&zones.lock, true);
1200
1201 size_t i;
1202 for (i = 0; i < count; i++) {
1203 size_t znum = find_zone(start + i, 1, 0);
1204 if (znum == (size_t) -1) /* PFN not found */
1205 continue;
1206
1207 zone_mark_unavailable(&zones.info[znum],
1208 start + i - zones.info[znum].base);
1209 }
1210
1211 irq_spinlock_unlock(&zones.lock, true);
1212}
1213
1214/** Initialize physical memory management.
1215 *
1216 */
1217void frame_init(void)
1218{
1219 if (config.cpu_active == 1) {
1220 zones.count = 0;
1221 irq_spinlock_initialize(&zones.lock, "frame.zones.lock");
1222 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1223 condvar_initialize(&mem_avail_cv);
1224 }
1225
1226 /* Tell the architecture to create some memory */
1227 frame_arch_init();
1228 if (config.cpu_active == 1) {
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));
1233
1234 size_t i;
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 }
1240
1241 if (ballocs.size)
1242 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1243 SIZE2FRAMES(ballocs.size));
1244
1245 /* Black list first frame, as allocating NULL would
1246 * fail in some places
1247 */
1248 frame_mark_unavailable(0, 1);
1249 }
1250}
1251
1252/** Return total size of all zones.
1253 *
1254 */
1255uint64_t zones_total_size(void)
1256{
1257 irq_spinlock_lock(&zones.lock, true);
1258
1259 uint64_t total = 0;
1260 size_t i;
1261 for (i = 0; i < zones.count; i++)
1262 total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
1263
1264 irq_spinlock_unlock(&zones.lock, true);
1265
1266 return total;
1267}
1268
1269void zones_stats(uint64_t *total, uint64_t *unavail, uint64_t *busy,
1270 uint64_t *free)
1271{
1272 ASSERT(total != NULL);
1273 ASSERT(unavail != NULL);
1274 ASSERT(busy != NULL);
1275 ASSERT(free != NULL);
1276
1277 irq_spinlock_lock(&zones.lock, true);
1278
1279 *total = 0;
1280 *unavail = 0;
1281 *busy = 0;
1282 *free = 0;
1283
1284 size_t i;
1285 for (i = 0; i < zones.count; i++) {
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);
1293 }
1294
1295 irq_spinlock_unlock(&zones.lock, true);
1296}
1297
1298/** Prints list of zones.
1299 *
1300 */
1301void zones_print_list(void)
1302{
1303#ifdef __32_BITS__
1304 printf("[nr] [base addr] [frames ] [flags ] [free frames ] [busy frames ]\n");
1305#endif
1306
1307#ifdef __64_BITS__
1308 printf("[nr] [base address ] [frames ] [flags ] [free frames ] [busy frames ]\n");
1309#endif
1310
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 */
1321
1322 size_t i;
1323 for (i = 0;; i++) {
1324 irq_spinlock_lock(&zones.lock, true);
1325
1326 if (i >= zones.count) {
1327 irq_spinlock_unlock(&zones.lock, true);
1328 break;
1329 }
1330
1331 uintptr_t base = PFN2ADDR(zones.info[i].base);
1332 size_t count = zones.info[i].count;
1333 zone_flags_t flags = zones.info[i].flags;
1334 size_t free_count = zones.info[i].free_count;
1335 size_t busy_count = zones.info[i].busy_count;
1336
1337 irq_spinlock_unlock(&zones.lock, true);
1338
1339 bool available = zone_flags_available(flags);
1340
1341 printf("%-4zu", i);
1342
1343#ifdef __32_BITS__
1344 printf(" %p", (void *) base);
1345#endif
1346
1347#ifdef __64_BITS__
1348 printf(" %p", (void *) base);
1349#endif
1350
1351 printf(" %12zu %c%c%c ", count,
1352 available ? 'A' : ' ',
1353 (flags & ZONE_RESERVED) ? 'R' : ' ',
1354 (flags & ZONE_FIRMWARE) ? 'F' : ' ');
1355
1356 if (available)
1357 printf("%14zu %14zu",
1358 free_count, busy_count);
1359
1360 printf("\n");
1361 }
1362}
1363
1364/** Prints zone details.
1365 *
1366 * @param num Zone base address or zone number.
1367 *
1368 */
1369void zone_print_one(size_t num)
1370{
1371 irq_spinlock_lock(&zones.lock, true);
1372 size_t znum = (size_t) -1;
1373
1374 size_t i;
1375 for (i = 0; i < zones.count; i++) {
1376 if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
1377 znum = i;
1378 break;
1379 }
1380 }
1381
1382 if (znum == (size_t) -1) {
1383 irq_spinlock_unlock(&zones.lock, true);
1384 printf("Zone not found.\n");
1385 return;
1386 }
1387
1388 uintptr_t base = PFN2ADDR(zones.info[i].base);
1389 zone_flags_t flags = zones.info[i].flags;
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;
1393
1394 irq_spinlock_unlock(&zones.lock, true);
1395
1396 bool available = zone_flags_available(flags);
1397
1398 uint64_t size;
1399 const char *size_suffix;
1400 bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
1401
1402 printf("Zone number: %zu\n", znum);
1403 printf("Zone base address: %p\n", (void *) base);
1404 printf("Zone size: %zu frames (%" PRIu64 " %s)\n", count,
1405 size, size_suffix);
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) {
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);
1420 }
1421}
1422
1423/** @}
1424 */
Note: See TracBrowser for help on using the repository browser.