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

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

Start tracking reservable memory after zones are created and possibly
merged. The initial amount of reservable memory is the number of free
frames at the time of call to reserve_init().

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