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

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

Introduce FRAME_LOW_16_GiB slab/frame allocator flag. When specified, the
allocators will not allocate memory above 16 GiB. Each architecture needs to
make sure not to merge zones from below and above 16 GiB. Allocations that
require memory below 16 GiB need to be altered to use this flag.

  • Property mode set to 100644
File size: 32.5 KB
Line 
1/*
2 * Copyright (c) 2001-2005 Jakub Jermar
3 * Copyright (c) 2005 Sergey Bondari
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup genericmm
31 * @{
32 */
33
34/**
35 * @file
36 * @brief Physical frame allocator.
37 *
38 * This file contains the physical frame allocator and memory zone management.
39 * The frame allocator is built on top of the buddy allocator.
40 *
41 * @see buddy.c
42 */
43
44/*
45 * Locking order
46 *
47 * In order to access particular zone, the process must first lock
48 * the zones.lock, then lock the zone and then unlock the zones.lock.
49 * This insures, that we can fiddle with the zones in runtime without
50 * affecting the processes.
51 *
52 */
53
54#include <arch/types.h>
55#include <mm/frame.h>
56#include <mm/as.h>
57#include <panic.h>
58#include <debug.h>
59#include <adt/list.h>
60#include <synch/spinlock.h>
61#include <synch/mutex.h>
62#include <synch/condvar.h>
63#include <arch/asm.h>
64#include <arch.h>
65#include <print.h>
66#include <align.h>
67#include <mm/slab.h>
68#include <bitops.h>
69#include <macros.h>
70#include <config.h>
71
72typedef struct {
73 count_t refcount; /**< tracking of shared frames */
74 uint8_t buddy_order; /**< buddy system block order */
75 link_t buddy_link; /**< link to the next free block inside one
76 order */
77 void *parent; /**< If allocated by slab, this points there */
78} frame_t;
79
80typedef struct {
81 SPINLOCK_DECLARE(lock); /**< this lock protects everything below */
82 pfn_t base; /**< frame_no of the first frame in the frames
83 array */
84 count_t count; /**< Size of zone */
85
86 frame_t *frames; /**< array of frame_t structures in this
87 zone */
88 count_t free_count; /**< number of free frame_t structures */
89 count_t busy_count; /**< number of busy frame_t structures */
90
91 buddy_system_t *buddy_system; /**< buddy system for the zone */
92 int flags;
93} zone_t;
94
95/*
96 * The zoneinfo.lock must be locked when accessing zoneinfo structure.
97 * Some of the attributes in zone_t structures are 'read-only'
98 */
99
100typedef struct {
101 SPINLOCK_DECLARE(lock);
102 unsigned int count;
103 zone_t *info[ZONES_MAX];
104} zones_t;
105
106static zones_t zones;
107
108/*
109 * Synchronization primitives used to sleep when there is no memory
110 * available.
111 */
112mutex_t mem_avail_mtx;
113condvar_t mem_avail_cv;
114unsigned long mem_avail_frames = 0; /**< Number of available frames. */
115unsigned long mem_avail_gen = 0; /**< Generation counter. */
116
117/********************/
118/* Helper functions */
119/********************/
120
121static inline index_t frame_index(zone_t *zone, frame_t *frame)
122{
123 return (index_t) (frame - zone->frames);
124}
125
126static inline index_t frame_index_abs(zone_t *zone, frame_t *frame)
127{
128 return (index_t) (frame - zone->frames) + zone->base;
129}
130
131static inline int frame_index_valid(zone_t *zone, index_t index)
132{
133 return (index < zone->count);
134}
135
136/** Compute pfn_t from frame_t pointer & zone pointer */
137static index_t make_frame_index(zone_t *zone, frame_t *frame)
138{
139 return (frame - zone->frames);
140}
141
142/** Initialize frame structure.
143 *
144 * @param frame Frame structure to be initialized.
145 */
146static void frame_initialize(frame_t *frame)
147{
148 frame->refcount = 1;
149 frame->buddy_order = 0;
150}
151
152/**********************/
153/* Zoneinfo functions */
154/**********************/
155
156/** Insert-sort zone into zones list.
157 *
158 * @param newzone New zone to be inserted into zone list.
159 * @return Zone number on success, -1 on error.
160 */
161static int zones_add_zone(zone_t *newzone)
162{
163 unsigned int i, j;
164 ipl_t ipl;
165 zone_t *z;
166
167 ipl = interrupts_disable();
168 spinlock_lock(&zones.lock);
169
170 /* Try to merge */
171 if (zones.count + 1 == ZONES_MAX) {
172 printf("Maximum zone count %u exceeded!\n", ZONES_MAX);
173 spinlock_unlock(&zones.lock);
174 interrupts_restore(ipl);
175 return -1;
176 }
177
178 for (i = 0; i < zones.count; i++) {
179 /* Check for overflow */
180 z = zones.info[i];
181 if (overlaps(newzone->base, newzone->count, z->base,
182 z->count)) {
183 printf("Zones overlap!\n");
184 return -1;
185 }
186 if (newzone->base < z->base)
187 break;
188 }
189
190 /* Move other zones up */
191 for (j = i; j < zones.count; j++)
192 zones.info[j + 1] = zones.info[j];
193
194 zones.info[i] = newzone;
195 zones.count++;
196
197 spinlock_unlock(&zones.lock);
198 interrupts_restore(ipl);
199
200 return i;
201}
202
203/** Try to find a zone where can we find the frame.
204 *
205 * Assume interrupts are disabled.
206 *
207 * @param frame Frame number contained in zone.
208 * @param pzone If not null, it is used as zone hint. Zone index is
209 * filled into the variable on success.
210 * @return Pointer to locked zone containing frame.
211 */
212static zone_t *find_zone_and_lock(pfn_t frame, unsigned int *pzone)
213{
214 unsigned int i;
215 unsigned int hint = pzone ? *pzone : 0;
216 zone_t *z;
217
218 spinlock_lock(&zones.lock);
219
220 if (hint >= zones.count)
221 hint = 0;
222
223 i = hint;
224 do {
225 z = zones.info[i];
226 spinlock_lock(&z->lock);
227 if (z->base <= frame && z->base + z->count > frame) {
228 /* Unlock the global lock */
229 spinlock_unlock(&zones.lock);
230 if (pzone)
231 *pzone = i;
232 return z;
233 }
234 spinlock_unlock(&z->lock);
235
236 i++;
237 if (i >= zones.count)
238 i = 0;
239 } while (i != hint);
240
241 spinlock_unlock(&zones.lock);
242 return NULL;
243}
244
245/** @return True if zone can allocate specified order */
246static int zone_can_alloc(zone_t *z, uint8_t order)
247{
248 return buddy_system_can_alloc(z->buddy_system, order);
249}
250
251/** Find and lock zone that can allocate order frames.
252 *
253 * Assume interrupts are disabled.
254 *
255 * @param order Size (2^order) of free space we are trying to find.
256 * @param flags Required flags of the target zone.
257 * @param pzone Pointer to preferred zone or NULL, on return contains
258 * zone number.
259 */
260static zone_t *
261find_free_zone_and_lock(uint8_t order, int flags, unsigned int *pzone)
262{
263 unsigned int i;
264 zone_t *z;
265 unsigned int hint = pzone ? *pzone : 0;
266
267 /* Mask off flags that are not applicable. */
268 flags &= FRAME_LOW_16_GiB;
269
270 spinlock_lock(&zones.lock);
271 if (hint >= zones.count)
272 hint = 0;
273 i = hint;
274 do {
275 z = zones.info[i];
276
277 spinlock_lock(&z->lock);
278
279 /*
280 * Check whether the zone meets the search criteria.
281 */
282 if ((z->flags & flags) == flags) {
283 /*
284 * Check if the zone has 2^order frames area available.
285 */
286 if (zone_can_alloc(z, order)) {
287 spinlock_unlock(&zones.lock);
288 if (pzone)
289 *pzone = i;
290 return z;
291 }
292 }
293 spinlock_unlock(&z->lock);
294 if (++i >= zones.count)
295 i = 0;
296 } while (i != hint);
297 spinlock_unlock(&zones.lock);
298 return NULL;
299}
300
301/**************************/
302/* Buddy system functions */
303/**************************/
304
305/** Buddy system find_block implementation.
306 *
307 * Find block that is parent of current list.
308 * That means go to lower addresses, until such block is found
309 *
310 * @param order Order of parent must be different then this
311 * parameter!!
312 */
313static link_t *zone_buddy_find_block(buddy_system_t *b, link_t *child,
314 uint8_t order)
315{
316 frame_t *frame;
317 zone_t *zone;
318 index_t index;
319
320 frame = list_get_instance(child, frame_t, buddy_link);
321 zone = (zone_t *) b->data;
322
323 index = frame_index(zone, frame);
324 do {
325 if (zone->frames[index].buddy_order != order) {
326 return &zone->frames[index].buddy_link;
327 }
328 } while(index-- > 0);
329 return NULL;
330}
331
332/** Buddy system find_buddy implementation.
333 *
334 * @param b Buddy system.
335 * @param block Block for which buddy should be found.
336 *
337 * @return Buddy for given block if found.
338 */
339static link_t *zone_buddy_find_buddy(buddy_system_t *b, link_t *block)
340{
341 frame_t *frame;
342 zone_t *zone;
343 index_t index;
344 bool is_left, is_right;
345
346 frame = list_get_instance(block, frame_t, buddy_link);
347 zone = (zone_t *) b->data;
348 ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),
349 frame->buddy_order));
350
351 is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
352 is_right = IS_BUDDY_RIGHT_BLOCK_ABS(zone, frame);
353
354 ASSERT(is_left ^ is_right);
355 if (is_left) {
356 index = (frame_index(zone, frame)) +
357 (1 << frame->buddy_order);
358 } else { /* if (is_right) */
359 index = (frame_index(zone, frame)) -
360 (1 << frame->buddy_order);
361 }
362
363 if (frame_index_valid(zone, index)) {
364 if (zone->frames[index].buddy_order == frame->buddy_order &&
365 zone->frames[index].refcount == 0) {
366 return &zone->frames[index].buddy_link;
367 }
368 }
369
370 return NULL;
371}
372
373/** Buddy system bisect implementation.
374 *
375 * @param b Buddy system.
376 * @param block Block to bisect.
377 *
378 * @return Right block.
379 */
380static link_t *zone_buddy_bisect(buddy_system_t *b, link_t *block)
381{
382 frame_t *frame_l, *frame_r;
383
384 frame_l = list_get_instance(block, frame_t, buddy_link);
385 frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
386
387 return &frame_r->buddy_link;
388}
389
390/** Buddy system coalesce implementation.
391 *
392 * @param b Buddy system.
393 * @param block_1 First block.
394 * @param block_2 First block's buddy.
395 *
396 * @return Coalesced block (actually block that represents lower
397 * address).
398 */
399static link_t *zone_buddy_coalesce(buddy_system_t *b, link_t *block_1,
400 link_t *block_2)
401{
402 frame_t *frame1, *frame2;
403
404 frame1 = list_get_instance(block_1, frame_t, buddy_link);
405 frame2 = list_get_instance(block_2, frame_t, buddy_link);
406
407 return frame1 < frame2 ? block_1 : block_2;
408}
409
410/** Buddy system set_order implementation.
411 *
412 * @param b Buddy system.
413 * @param block Buddy system block.
414 * @param order Order to set.
415 */
416static void zone_buddy_set_order(buddy_system_t *b, link_t *block,
417 uint8_t order)
418{
419 frame_t *frame;
420 frame = list_get_instance(block, frame_t, buddy_link);
421 frame->buddy_order = order;
422}
423
424/** Buddy system get_order implementation.
425 *
426 * @param b Buddy system.
427 * @param block Buddy system block.
428 *
429 * @return Order of block.
430 */
431static uint8_t zone_buddy_get_order(buddy_system_t *b, link_t *block)
432{
433 frame_t *frame;
434 frame = list_get_instance(block, frame_t, buddy_link);
435 return frame->buddy_order;
436}
437
438/** Buddy system mark_busy implementation.
439 *
440 * @param b Buddy system.
441 * @param block Buddy system block.
442 */
443static void zone_buddy_mark_busy(buddy_system_t *b, link_t * block)
444{
445 frame_t * frame;
446
447 frame = list_get_instance(block, frame_t, buddy_link);
448 frame->refcount = 1;
449}
450
451/** Buddy system mark_available implementation.
452 *
453 * @param b Buddy system.
454 * @param block Buddy system block.
455 */
456static void zone_buddy_mark_available(buddy_system_t *b, link_t *block)
457{
458 frame_t *frame;
459 frame = list_get_instance(block, frame_t, buddy_link);
460 frame->refcount = 0;
461}
462
463static buddy_system_operations_t zone_buddy_system_operations = {
464 .find_buddy = zone_buddy_find_buddy,
465 .bisect = zone_buddy_bisect,
466 .coalesce = zone_buddy_coalesce,
467 .set_order = zone_buddy_set_order,
468 .get_order = zone_buddy_get_order,
469 .mark_busy = zone_buddy_mark_busy,
470 .mark_available = zone_buddy_mark_available,
471 .find_block = zone_buddy_find_block
472};
473
474/******************/
475/* Zone functions */
476/******************/
477
478/** Allocate frame in particular zone.
479 *
480 * Assume zone is locked.
481 * Panics if allocation is impossible.
482 *
483 * @param zone Zone to allocate from.
484 * @param order Allocate exactly 2^order frames.
485 *
486 * @return Frame index in zone.
487 *
488 */
489static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
490{
491 pfn_t v;
492 link_t *tmp;
493 frame_t *frame;
494
495 /* Allocate frames from zone buddy system */
496 tmp = buddy_system_alloc(zone->buddy_system, order);
497
498 ASSERT(tmp);
499
500 /* Update zone information. */
501 zone->free_count -= (1 << order);
502 zone->busy_count += (1 << order);
503
504 /* Frame will be actually a first frame of the block. */
505 frame = list_get_instance(tmp, frame_t, buddy_link);
506
507 /* get frame address */
508 v = make_frame_index(zone, frame);
509 return v;
510}
511
512/** Free frame from zone.
513 *
514 * Assume zone is locked.
515 *
516 * @param zone Pointer to zone from which the frame is to be freed.
517 * @param frame_idx Frame index relative to zone.
518 */
519static void zone_frame_free(zone_t *zone, index_t frame_idx)
520{
521 frame_t *frame;
522 uint8_t order;
523
524 frame = &zone->frames[frame_idx];
525
526 /* remember frame order */
527 order = frame->buddy_order;
528
529 ASSERT(frame->refcount);
530
531 if (!--frame->refcount) {
532 buddy_system_free(zone->buddy_system, &frame->buddy_link);
533
534 /* Update zone information. */
535 zone->free_count += (1 << order);
536 zone->busy_count -= (1 << order);
537 }
538}
539
540/** Return frame from zone. */
541static frame_t *zone_get_frame(zone_t *zone, index_t frame_idx)
542{
543 ASSERT(frame_idx < zone->count);
544 return &zone->frames[frame_idx];
545}
546
547/** Mark frame in zone unavailable to allocation. */
548static void zone_mark_unavailable(zone_t *zone, index_t frame_idx)
549{
550 frame_t *frame;
551 link_t *link;
552
553 frame = zone_get_frame(zone, frame_idx);
554 if (frame->refcount)
555 return;
556 link = buddy_system_alloc_block(zone->buddy_system,
557 &frame->buddy_link);
558 ASSERT(link);
559 zone->free_count--;
560
561 mutex_lock(&mem_avail_mtx);
562 mem_avail_frames--;
563 mutex_unlock(&mem_avail_mtx);
564}
565
566/** Join two zones.
567 *
568 * Expect zone_t *z to point to space at least zone_conf_size large.
569 *
570 * Assume z1 & z2 are locked.
571 *
572 * @param z Target zone structure pointer.
573 * @param z1 Zone to merge.
574 * @param z2 Zone to merge.
575 */
576static void _zone_merge(zone_t *z, zone_t *z1, zone_t *z2)
577{
578 uint8_t max_order;
579 unsigned int i;
580 int z2idx;
581 pfn_t frame_idx;
582 frame_t *frame;
583
584 ASSERT(!overlaps(z1->base, z1->count, z2->base, z2->count));
585 ASSERT(z1->base < z2->base);
586
587 spinlock_initialize(&z->lock, "zone_lock");
588 z->base = z1->base;
589 z->count = z2->base + z2->count - z1->base;
590 z->flags = z1->flags & z2->flags;
591
592 z->free_count = z1->free_count + z2->free_count;
593 z->busy_count = z1->busy_count + z2->busy_count;
594
595 max_order = fnzb(z->count);
596
597 z->buddy_system = (buddy_system_t *) &z[1];
598 buddy_system_create(z->buddy_system, max_order,
599 &zone_buddy_system_operations, (void *) z);
600
601 z->frames = (frame_t *)((uint8_t *) z->buddy_system +
602 buddy_conf_size(max_order));
603 for (i = 0; i < z->count; i++) {
604 /* This marks all frames busy */
605 frame_initialize(&z->frames[i]);
606 }
607 /* Copy frames from both zones to preserve full frame orders,
608 * parents etc. Set all free frames with refcount=0 to 1, because
609 * we add all free frames to buddy allocator later again, clear
610 * order to 0. Don't set busy frames with refcount=0, as they
611 * will not be reallocated during merge and it would make later
612 * problems with allocation/free.
613 */
614 for (i = 0; i < z1->count; i++)
615 z->frames[i] = z1->frames[i];
616 for (i = 0; i < z2->count; i++) {
617 z2idx = i + (z2->base - z1->base);
618 z->frames[z2idx] = z2->frames[i];
619 }
620 i = 0;
621 while (i < z->count) {
622 if (z->frames[i].refcount) {
623 /* skip busy frames */
624 i += 1 << z->frames[i].buddy_order;
625 } else { /* Free frames, set refcount=1 */
626 /* All free frames have refcount=0, we need not
627 * to check the order */
628 z->frames[i].refcount = 1;
629 z->frames[i].buddy_order = 0;
630 i++;
631 }
632 }
633 /* Add free blocks from the 2 original zones */
634 while (zone_can_alloc(z1, 0)) {
635 frame_idx = zone_frame_alloc(z1, 0);
636 frame = &z->frames[frame_idx];
637 frame->refcount = 0;
638 buddy_system_free(z->buddy_system, &frame->buddy_link);
639 }
640 while (zone_can_alloc(z2, 0)) {
641 frame_idx = zone_frame_alloc(z2, 0);
642 frame = &z->frames[frame_idx + (z2->base - z1->base)];
643 frame->refcount = 0;
644 buddy_system_free(z->buddy_system, &frame->buddy_link);
645 }
646}
647
648/** Return old configuration frames into the zone.
649 *
650 * We have several cases
651 * - the conf. data is outside of zone -> exit, shall we call frame_free??
652 * - the conf. data was created by zone_create or
653 * updated with reduce_region -> free every frame
654 *
655 * @param newzone The actual zone where freeing should occur.
656 * @param oldzone Pointer to old zone configuration data that should
657 * be freed from new zone.
658 */
659static void return_config_frames(zone_t *newzone, zone_t *oldzone)
660{
661 pfn_t pfn;
662 frame_t *frame;
663 count_t cframes;
664 unsigned int i;
665
666 pfn = ADDR2PFN((uintptr_t)KA2PA(oldzone));
667 cframes = SIZE2FRAMES(zone_conf_size(oldzone->count));
668
669 if (pfn < newzone->base || pfn >= newzone->base + newzone->count)
670 return;
671
672 frame = &newzone->frames[pfn - newzone->base];
673 ASSERT(!frame->buddy_order);
674
675 for (i = 0; i < cframes; i++) {
676 newzone->busy_count++;
677 zone_frame_free(newzone, pfn+i-newzone->base);
678 }
679}
680
681/** Reduce allocated block to count of order 0 frames.
682 *
683 * The allocated block need 2^order frames of space. Reduce all frames
684 * in block to order 0 and free the unneeded frames. This means, that
685 * when freeing the previously allocated block starting with frame_idx,
686 * you have to free every frame.
687 *
688 * @param zone
689 * @param frame_idx Index to block.
690 * @param count Allocated space in block.
691 */
692static void zone_reduce_region(zone_t *zone, pfn_t frame_idx, count_t count)
693{
694 count_t i;
695 uint8_t order;
696 frame_t *frame;
697
698 ASSERT(frame_idx + count < zone->count);
699
700 order = zone->frames[frame_idx].buddy_order;
701 ASSERT((count_t) (1 << order) >= count);
702
703 /* Reduce all blocks to order 0 */
704 for (i = 0; i < (count_t) (1 << order); i++) {
705 frame = &zone->frames[i + frame_idx];
706 frame->buddy_order = 0;
707 if (!frame->refcount)
708 frame->refcount = 1;
709 ASSERT(frame->refcount == 1);
710 }
711 /* Free unneeded frames */
712 for (i = count; i < (count_t) (1 << order); i++) {
713 zone_frame_free(zone, i + frame_idx);
714 }
715}
716
717/** Merge zones z1 and z2.
718 *
719 * - the zones must be 2 zones with no zone existing in between,
720 * which means that z2 = z1+1
721 *
722 * - When you create a new zone, the frame allocator configuration does
723 * not to be 2^order size. Once the allocator is running it is no longer
724 * possible, merged configuration data occupies more space :-/
725 */
726void zone_merge(unsigned int z1, unsigned int z2)
727{
728 ipl_t ipl;
729 zone_t *zone1, *zone2, *newzone;
730 unsigned int cframes;
731 uint8_t order;
732 unsigned int i;
733 pfn_t pfn;
734
735 ipl = interrupts_disable();
736 spinlock_lock(&zones.lock);
737
738 if ((z1 >= zones.count) || (z2 >= zones.count))
739 goto errout;
740 /* We can join only 2 zones with none existing inbetween */
741 if (z2 - z1 != 1)
742 goto errout;
743
744 zone1 = zones.info[z1];
745 zone2 = zones.info[z2];
746 spinlock_lock(&zone1->lock);
747 spinlock_lock(&zone2->lock);
748
749 cframes = SIZE2FRAMES(zone_conf_size(zone2->base + zone2->count -
750 zone1->base));
751 if (cframes == 1)
752 order = 0;
753 else
754 order = fnzb(cframes - 1) + 1;
755
756 /* Allocate zonedata inside one of the zones */
757 if (zone_can_alloc(zone1, order))
758 pfn = zone1->base + zone_frame_alloc(zone1, order);
759 else if (zone_can_alloc(zone2, order))
760 pfn = zone2->base + zone_frame_alloc(zone2, order);
761 else
762 goto errout2;
763
764 newzone = (zone_t *) PA2KA(PFN2ADDR(pfn));
765
766 _zone_merge(newzone, zone1, zone2);
767
768 /* Free unneeded config frames */
769 zone_reduce_region(newzone, pfn - newzone->base, cframes);
770 /* Subtract zone information from busy frames */
771 newzone->busy_count -= cframes;
772
773 /* Replace existing zones in zoneinfo list */
774 zones.info[z1] = newzone;
775 for (i = z2 + 1; i < zones.count; i++)
776 zones.info[i - 1] = zones.info[i];
777 zones.count--;
778
779 /* Free old zone information */
780 return_config_frames(newzone, zone1);
781 return_config_frames(newzone, zone2);
782errout2:
783 /* Nobody is allowed to enter to zone, so we are safe
784 * to touch the spinlocks last time */
785 spinlock_unlock(&zone1->lock);
786 spinlock_unlock(&zone2->lock);
787errout:
788 spinlock_unlock(&zones.lock);
789 interrupts_restore(ipl);
790}
791
792/** Merge all zones into one big zone.
793 *
794 * It is reasonable to do this on systems whose bios reports parts in chunks,
795 * so that we could have 1 zone (it's faster).
796 */
797void zone_merge_all(void)
798{
799 int count = zones.count;
800
801 while (zones.count > 1 && --count) {
802 zone_merge(0, 1);
803 break;
804 }
805}
806
807/** Create new frame zone.
808 *
809 * @param start Physical address of the first frame within the zone.
810 * @param count Count of frames in zone.
811 * @param z Address of configuration information of zone.
812 * @param flags Zone flags.
813 *
814 * @return Initialized zone.
815 */
816static void zone_construct(pfn_t start, count_t count, zone_t *z, int flags)
817{
818 unsigned int i;
819 uint8_t max_order;
820
821 spinlock_initialize(&z->lock, "zone_lock");
822 z->base = start;
823 z->count = count;
824
825 /* Mask off flags that are calculated automatically. */
826 flags &= ~FRAME_LOW_16_GiB;
827 /* Determine calculated flags. */
828 if (z->base + count < (1ULL << (34 - FRAME_WIDTH))) /* 16 GiB */
829 flags |= FRAME_LOW_16_GiB;
830
831 z->flags = flags;
832
833 z->free_count = count;
834 z->busy_count = 0;
835
836 /*
837 * Compute order for buddy system, initialize
838 */
839 max_order = fnzb(count);
840 z->buddy_system = (buddy_system_t *)&z[1];
841
842 buddy_system_create(z->buddy_system, max_order,
843 &zone_buddy_system_operations, (void *) z);
844
845 /* Allocate frames _after_ the conframe */
846 /* Check sizes */
847 z->frames = (frame_t *)((uint8_t *) z->buddy_system +
848 buddy_conf_size(max_order));
849 for (i = 0; i < count; i++) {
850 frame_initialize(&z->frames[i]);
851 }
852
853 /* Stuffing frames */
854 for (i = 0; i < count; i++) {
855 z->frames[i].refcount = 0;
856 buddy_system_free(z->buddy_system, &z->frames[i].buddy_link);
857 }
858}
859
860/** Compute configuration data size for zone.
861 *
862 * @param count Size of zone in frames.
863 * @return Size of zone configuration info (in bytes).
864 */
865uintptr_t zone_conf_size(count_t count)
866{
867 int size = sizeof(zone_t) + count * sizeof(frame_t);
868 int max_order;
869
870 max_order = fnzb(count);
871 size += buddy_conf_size(max_order);
872 return size;
873}
874
875/** Create and add zone to system.
876 *
877 * @param start First frame number (absolute).
878 * @param count Size of zone in frames.
879 * @param confframe Where configuration frames are supposed to be.
880 * Automatically checks, that we will not disturb the
881 * kernel and possibly init. If confframe is given
882 * _outside_ this zone, it is expected, that the area is
883 * already marked BUSY and big enough to contain
884 * zone_conf_size() amount of data. If the confframe is
885 * inside the area, the zone free frame information is
886 * modified not to include it.
887 *
888 * @return Zone number or -1 on error.
889 */
890int zone_create(pfn_t start, count_t count, pfn_t confframe, int flags)
891{
892 zone_t *z;
893 uintptr_t addr;
894 count_t confcount;
895 unsigned int i;
896 int znum;
897
898 /* Theoretically we could have here 0, practically make sure
899 * nobody tries to do that. If some platform requires, remove
900 * the assert
901 */
902 ASSERT(confframe);
903 /* If conframe is supposed to be inside our zone, then make sure
904 * it does not span kernel & init
905 */
906 confcount = SIZE2FRAMES(zone_conf_size(count));
907 if (confframe >= start && confframe < start + count) {
908 for (; confframe < start + count; confframe++) {
909 addr = PFN2ADDR(confframe);
910 if (overlaps(addr, PFN2ADDR(confcount),
911 KA2PA(config.base), config.kernel_size))
912 continue;
913
914 if (overlaps(addr, PFN2ADDR(confcount),
915 KA2PA(config.stack_base), config.stack_size))
916 continue;
917
918 bool overlap = false;
919 count_t i;
920 for (i = 0; i < init.cnt; i++)
921 if (overlaps(addr, PFN2ADDR(confcount),
922 KA2PA(init.tasks[i].addr),
923 init.tasks[i].size)) {
924 overlap = true;
925 break;
926 }
927 if (overlap)
928 continue;
929
930 break;
931 }
932 if (confframe >= start + count)
933 panic("Cannot find configuration data for zone.");
934 }
935
936 z = (zone_t *) PA2KA(PFN2ADDR(confframe));
937 zone_construct(start, count, z, flags);
938 znum = zones_add_zone(z);
939 if (znum == -1)
940 return -1;
941
942 mutex_lock(&mem_avail_mtx);
943 mem_avail_frames += count;
944 mutex_unlock(&mem_avail_mtx);
945
946 /* If confdata in zone, mark as unavailable */
947 if (confframe >= start && confframe < start + count)
948 for (i = confframe; i < confframe + confcount; i++) {
949 zone_mark_unavailable(z, i - z->base);
950 }
951
952 return znum;
953}
954
955/***************************************/
956/* Frame functions */
957
958/** Set parent of frame. */
959void frame_set_parent(pfn_t pfn, void *data, unsigned int hint)
960{
961 zone_t *zone = find_zone_and_lock(pfn, &hint);
962
963 ASSERT(zone);
964
965 zone_get_frame(zone, pfn - zone->base)->parent = data;
966 spinlock_unlock(&zone->lock);
967}
968
969void *frame_get_parent(pfn_t pfn, unsigned int hint)
970{
971 zone_t *zone = find_zone_and_lock(pfn, &hint);
972 void *res;
973
974 ASSERT(zone);
975 res = zone_get_frame(zone, pfn - zone->base)->parent;
976
977 spinlock_unlock(&zone->lock);
978 return res;
979}
980
981/** Allocate power-of-two frames of physical memory.
982 *
983 * @param order Allocate exactly 2^order frames.
984 * @param flags Flags for host zone selection and address processing.
985 * @param pzone Preferred zone.
986 *
987 * @return Physical address of the allocated frame.
988 *
989 */
990void *frame_alloc_generic(uint8_t order, int flags, unsigned int *pzone)
991{
992 ipl_t ipl;
993 int freed;
994 pfn_t v;
995 zone_t *zone;
996 unsigned long gen = 0;
997
998loop:
999 ipl = interrupts_disable();
1000
1001 /*
1002 * First, find suitable frame zone.
1003 */
1004 zone = find_free_zone_and_lock(order, flags, pzone);
1005
1006 /* If no memory, reclaim some slab memory,
1007 if it does not help, reclaim all */
1008 if (!zone && !(flags & FRAME_NO_RECLAIM)) {
1009 freed = slab_reclaim(0);
1010 if (freed)
1011 zone = find_free_zone_and_lock(order, flags, pzone);
1012 if (!zone) {
1013 freed = slab_reclaim(SLAB_RECLAIM_ALL);
1014 if (freed)
1015 zone = find_free_zone_and_lock(order, flags,
1016 pzone);
1017 }
1018 }
1019 if (!zone) {
1020 /*
1021 * Sleep until some frames are available again.
1022 */
1023 if (flags & FRAME_ATOMIC) {
1024 interrupts_restore(ipl);
1025 return 0;
1026 }
1027
1028#ifdef CONFIG_DEBUG
1029 unsigned long avail;
1030
1031 mutex_lock(&mem_avail_mtx);
1032 avail = mem_avail_frames;
1033 mutex_unlock(&mem_avail_mtx);
1034
1035 printf("Thread %" PRIu64 " waiting for %u frames, "
1036 "%u available.\n", THREAD->tid, 1ULL << order, avail);
1037#endif
1038
1039 mutex_lock(&mem_avail_mtx);
1040 while ((mem_avail_frames < (1ULL << order)) ||
1041 gen == mem_avail_gen)
1042 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
1043 gen = mem_avail_gen;
1044 mutex_unlock(&mem_avail_mtx);
1045
1046#ifdef CONFIG_DEBUG
1047 mutex_lock(&mem_avail_mtx);
1048 avail = mem_avail_frames;
1049 mutex_unlock(&mem_avail_mtx);
1050
1051 printf("Thread %" PRIu64 " woken up, %u frames available.\n",
1052 THREAD->tid, avail);
1053#endif
1054
1055 interrupts_restore(ipl);
1056 goto loop;
1057 }
1058
1059 v = zone_frame_alloc(zone, order);
1060 v += zone->base;
1061
1062 spinlock_unlock(&zone->lock);
1063
1064 mutex_lock(&mem_avail_mtx);
1065 mem_avail_frames -= (1ULL << order);
1066 mutex_unlock(&mem_avail_mtx);
1067
1068 interrupts_restore(ipl);
1069
1070 if (flags & FRAME_KA)
1071 return (void *)PA2KA(PFN2ADDR(v));
1072 return (void *)PFN2ADDR(v);
1073}
1074
1075/** Free a frame.
1076 *
1077 * Find respective frame structure for supplied physical frame address.
1078 * Decrement frame reference count.
1079 * If it drops to zero, move the frame structure to free list.
1080 *
1081 * @param frame Physical Address of of the frame to be freed.
1082 */
1083void frame_free(uintptr_t frame)
1084{
1085 ipl_t ipl;
1086 zone_t *zone;
1087 pfn_t pfn = ADDR2PFN(frame);
1088
1089 ipl = interrupts_disable();
1090
1091 /*
1092 * First, find host frame zone for addr.
1093 */
1094 zone = find_zone_and_lock(pfn, NULL);
1095 ASSERT(zone);
1096
1097 zone_frame_free(zone, pfn - zone->base);
1098
1099 spinlock_unlock(&zone->lock);
1100
1101 /*
1102 * Signal that some memory has been freed.
1103 */
1104 mutex_lock(&mem_avail_mtx);
1105 mem_avail_frames++;
1106 mem_avail_gen++;
1107 condvar_broadcast(&mem_avail_cv);
1108 mutex_unlock(&mem_avail_mtx);
1109
1110 interrupts_restore(ipl);
1111}
1112
1113/** Add reference to frame.
1114 *
1115 * Find respective frame structure for supplied PFN and
1116 * increment frame reference count.
1117 *
1118 * @param pfn Frame number of the frame to be freed.
1119 */
1120void frame_reference_add(pfn_t pfn)
1121{
1122 ipl_t ipl;
1123 zone_t *zone;
1124 frame_t *frame;
1125
1126 ipl = interrupts_disable();
1127
1128 /*
1129 * First, find host frame zone for addr.
1130 */
1131 zone = find_zone_and_lock(pfn, NULL);
1132 ASSERT(zone);
1133
1134 frame = &zone->frames[pfn - zone->base];
1135 frame->refcount++;
1136
1137 spinlock_unlock(&zone->lock);
1138 interrupts_restore(ipl);
1139}
1140
1141/** Mark given range unavailable in frame zones. */
1142void frame_mark_unavailable(pfn_t start, count_t count)
1143{
1144 unsigned int i;
1145 zone_t *zone;
1146 unsigned int prefzone = 0;
1147
1148 for (i = 0; i < count; i++) {
1149 zone = find_zone_and_lock(start + i, &prefzone);
1150 if (!zone) /* PFN not found */
1151 continue;
1152 zone_mark_unavailable(zone, start + i - zone->base);
1153
1154 spinlock_unlock(&zone->lock);
1155 }
1156}
1157
1158/** Initialize physical memory management. */
1159void frame_init(void)
1160{
1161 if (config.cpu_active == 1) {
1162 zones.count = 0;
1163 spinlock_initialize(&zones.lock, "zones.lock");
1164 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1165 condvar_initialize(&mem_avail_cv);
1166 }
1167 /* Tell the architecture to create some memory */
1168 frame_arch_init();
1169 if (config.cpu_active == 1) {
1170 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1171 SIZE2FRAMES(config.kernel_size));
1172 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
1173 SIZE2FRAMES(config.stack_size));
1174
1175 count_t i;
1176 for (i = 0; i < init.cnt; i++) {
1177 pfn_t pfn = ADDR2PFN(KA2PA(init.tasks[i].addr));
1178 frame_mark_unavailable(pfn,
1179 SIZE2FRAMES(init.tasks[i].size));
1180 }
1181
1182 if (ballocs.size)
1183 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1184 SIZE2FRAMES(ballocs.size));
1185
1186 /* Black list first frame, as allocating NULL would
1187 * fail in some places */
1188 frame_mark_unavailable(0, 1);
1189 }
1190}
1191
1192
1193/** Return total size of all zones. */
1194uint64_t zone_total_size(void)
1195{
1196 zone_t *zone = NULL;
1197 unsigned int i;
1198 ipl_t ipl;
1199 uint64_t total = 0;
1200
1201 ipl = interrupts_disable();
1202 spinlock_lock(&zones.lock);
1203
1204 for (i = 0; i < zones.count; i++) {
1205 zone = zones.info[i];
1206 spinlock_lock(&zone->lock);
1207 total += (uint64_t) FRAMES2SIZE(zone->count);
1208 spinlock_unlock(&zone->lock);
1209 }
1210
1211 spinlock_unlock(&zones.lock);
1212 interrupts_restore(ipl);
1213
1214 return total;
1215}
1216
1217/** Prints list of zones. */
1218void zone_print_list(void)
1219{
1220 zone_t *zone = NULL;
1221 unsigned int i;
1222 ipl_t ipl;
1223
1224#ifdef __32_BITS__
1225 printf("# base address free frames busy frames\n");
1226 printf("-- ------------ ------------ ------------\n");
1227#endif
1228
1229#ifdef __64_BITS__
1230 printf("# base address free frames busy frames\n");
1231 printf("-- -------------------- ------------ ------------\n");
1232#endif
1233
1234 /*
1235 * Because printing may require allocation of memory, we may not hold
1236 * the frame allocator locks when printing zone statistics. Therefore,
1237 * we simply gather the statistics under the protection of the locks and
1238 * print the statistics when the locks have been released.
1239 *
1240 * When someone adds/removes zones while we are printing the statistics,
1241 * we may end up with inaccurate output (e.g. a zone being skipped from
1242 * the listing).
1243 */
1244
1245 for (i = 0; ; i++) {
1246 uintptr_t base;
1247 count_t free_count;
1248 count_t busy_count;
1249
1250 ipl = interrupts_disable();
1251 spinlock_lock(&zones.lock);
1252
1253 if (i >= zones.count) {
1254 spinlock_unlock(&zones.lock);
1255 interrupts_restore(ipl);
1256 break;
1257 }
1258
1259 zone = zones.info[i];
1260 spinlock_lock(&zone->lock);
1261
1262 base = PFN2ADDR(zone->base);
1263 free_count = zone->free_count;
1264 busy_count = zone->busy_count;
1265
1266 spinlock_unlock(&zone->lock);
1267
1268 spinlock_unlock(&zones.lock);
1269 interrupts_restore(ipl);
1270
1271#ifdef __32_BITS__
1272 printf("%-2u %10p %12" PRIc " %12" PRIc "\n", i, base,
1273 free_count, busy_count);
1274#endif
1275
1276#ifdef __64_BITS__
1277 printf("%-2u %18p %12" PRIc " %12" PRIc "\n", i, base,
1278 free_count, busy_count);
1279#endif
1280
1281 }
1282}
1283
1284/** Prints zone details.
1285 *
1286 * @param num Zone base address or zone number.
1287 */
1288void zone_print_one(unsigned int num)
1289{
1290 zone_t *zone = NULL;
1291 ipl_t ipl;
1292 unsigned int i;
1293 uintptr_t base;
1294 count_t count;
1295 count_t busy_count;
1296 count_t free_count;
1297
1298 ipl = interrupts_disable();
1299 spinlock_lock(&zones.lock);
1300
1301 for (i = 0; i < zones.count; i++) {
1302 if ((i == num) || (PFN2ADDR(zones.info[i]->base) == num)) {
1303 zone = zones.info[i];
1304 break;
1305 }
1306 }
1307 if (!zone) {
1308 spinlock_unlock(&zones.lock);
1309 interrupts_restore(ipl);
1310 printf("Zone not found.\n");
1311 return;
1312 }
1313
1314 spinlock_lock(&zone->lock);
1315 base = PFN2ADDR(zone->base);
1316 count = zone->count;
1317 busy_count = zone->busy_count;
1318 free_count = zone->free_count;
1319 spinlock_unlock(&zone->lock);
1320 spinlock_unlock(&zones.lock);
1321 interrupts_restore(ipl);
1322
1323 printf("Zone base address: %p\n", base);
1324 printf("Zone size: %" PRIc " frames (%" PRIs " KiB)\n", count,
1325 SIZE2KB(FRAMES2SIZE(count)));
1326 printf("Allocated space: %" PRIc " frames (%" PRIs " KiB)\n",
1327 busy_count, SIZE2KB(FRAMES2SIZE(busy_count)));
1328 printf("Available space: %" PRIc " frames (%" PRIs " KiB)\n",
1329 free_count, SIZE2KB(FRAMES2SIZE(free_count)));
1330}
1331
1332/** @}
1333 */
1334
Note: See TracBrowser for help on using the repository browser.