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

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

Finer grained waiting for memory with consideration of the number
of available memory frames.

  • Property mode set to 100644
File size: 31.6 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/**
204 * Try to find a zone where can we find the frame.
205 *
206 * Assume interrupts are disabled.
207 *
208 * @param frame Frame number contained in zone.
209 * @param pzone If not null, it is used as zone hint. Zone index is
210 * filled into the variable on success.
211 * @return Pointer to locked zone containing frame.
212 */
213static zone_t *find_zone_and_lock(pfn_t frame, unsigned int *pzone)
214{
215 unsigned int i;
216 unsigned int hint = pzone ? *pzone : 0;
217 zone_t *z;
218
219 spinlock_lock(&zones.lock);
220
221 if (hint >= zones.count)
222 hint = 0;
223
224 i = hint;
225 do {
226 z = zones.info[i];
227 spinlock_lock(&z->lock);
228 if (z->base <= frame && z->base + z->count > frame) {
229 /* Unlock the global lock */
230 spinlock_unlock(&zones.lock);
231 if (pzone)
232 *pzone = i;
233 return z;
234 }
235 spinlock_unlock(&z->lock);
236
237 i++;
238 if (i >= zones.count)
239 i = 0;
240 } while(i != hint);
241
242 spinlock_unlock(&zones.lock);
243 return NULL;
244}
245
246/** @return True if zone can allocate specified order */
247static int zone_can_alloc(zone_t *z, uint8_t order)
248{
249 return buddy_system_can_alloc(z->buddy_system, order);
250}
251
252/** Find and lock zone that can allocate order frames.
253 *
254 * Assume interrupts are disabled.
255 *
256 * @param order Size (2^order) of free space we are trying to find.
257 * @param pzone Pointer to preferred zone or NULL, on return contains
258 * zone number.
259 */
260static zone_t *find_free_zone_and_lock(uint8_t order, unsigned int *pzone)
261{
262 unsigned int i;
263 zone_t *z;
264 unsigned int hint = pzone ? *pzone : 0;
265
266 spinlock_lock(&zones.lock);
267 if (hint >= zones.count)
268 hint = 0;
269 i = hint;
270 do {
271 z = zones.info[i];
272
273 spinlock_lock(&z->lock);
274
275 /* Check if the zone has 2^order frames area available */
276 if (zone_can_alloc(z, order)) {
277 spinlock_unlock(&zones.lock);
278 if (pzone)
279 *pzone = i;
280 return z;
281 }
282 spinlock_unlock(&z->lock);
283 if (++i >= zones.count)
284 i = 0;
285 } while(i != hint);
286 spinlock_unlock(&zones.lock);
287 return NULL;
288}
289
290/**************************/
291/* Buddy system functions */
292/**************************/
293
294/** Buddy system find_block implementation.
295 *
296 * Find block that is parent of current list.
297 * That means go to lower addresses, until such block is found
298 *
299 * @param order Order of parent must be different then this
300 * parameter!!
301 */
302static link_t *zone_buddy_find_block(buddy_system_t *b, link_t *child,
303 uint8_t order)
304{
305 frame_t *frame;
306 zone_t *zone;
307 index_t index;
308
309 frame = list_get_instance(child, frame_t, buddy_link);
310 zone = (zone_t *) b->data;
311
312 index = frame_index(zone, frame);
313 do {
314 if (zone->frames[index].buddy_order != order) {
315 return &zone->frames[index].buddy_link;
316 }
317 } while(index-- > 0);
318 return NULL;
319}
320
321static void zone_buddy_print_id(buddy_system_t *b, link_t *block)
322{
323 frame_t *frame;
324 zone_t *zone;
325 index_t index;
326
327 frame = list_get_instance(block, frame_t, buddy_link);
328 zone = (zone_t *) b->data;
329 index = frame_index(zone, frame);
330 printf("%" PRIi, index);
331}
332
333/** Buddy system find_buddy implementation.
334 *
335 * @param b Buddy system.
336 * @param block Block for which buddy should be found.
337 *
338 * @return Buddy for given block if found.
339 */
340static link_t *zone_buddy_find_buddy(buddy_system_t *b, link_t *block)
341{
342 frame_t *frame;
343 zone_t *zone;
344 index_t index;
345 bool is_left, is_right;
346
347 frame = list_get_instance(block, frame_t, buddy_link);
348 zone = (zone_t *) b->data;
349 ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),
350 frame->buddy_order));
351
352 is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
353 is_right = IS_BUDDY_RIGHT_BLOCK_ABS(zone, frame);
354
355 ASSERT(is_left ^ is_right);
356 if (is_left) {
357 index = (frame_index(zone, frame)) +
358 (1 << frame->buddy_order);
359 } else { /* if (is_right) */
360 index = (frame_index(zone, frame)) -
361 (1 << frame->buddy_order);
362 }
363
364 if (frame_index_valid(zone, index)) {
365 if (zone->frames[index].buddy_order == frame->buddy_order &&
366 zone->frames[index].refcount == 0) {
367 return &zone->frames[index].buddy_link;
368 }
369 }
370
371 return NULL;
372}
373
374/** Buddy system bisect implementation.
375 *
376 * @param b Buddy system.
377 * @param block Block to bisect.
378 *
379 * @return Right block.
380 */
381static link_t *zone_buddy_bisect(buddy_system_t *b, link_t *block)
382{
383 frame_t *frame_l, *frame_r;
384
385 frame_l = list_get_instance(block, frame_t, buddy_link);
386 frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
387
388 return &frame_r->buddy_link;
389}
390
391/** Buddy system coalesce implementation.
392 *
393 * @param b Buddy system.
394 * @param block_1 First block.
395 * @param block_2 First block's buddy.
396 *
397 * @return Coalesced block (actually block that represents lower
398 * address).
399 */
400static link_t *zone_buddy_coalesce(buddy_system_t *b, link_t *block_1,
401 link_t *block_2)
402{
403 frame_t *frame1, *frame2;
404
405 frame1 = list_get_instance(block_1, frame_t, buddy_link);
406 frame2 = list_get_instance(block_2, frame_t, buddy_link);
407
408 return frame1 < frame2 ? block_1 : block_2;
409}
410
411/** Buddy system set_order implementation.
412 *
413 * @param b Buddy system.
414 * @param block Buddy system block.
415 * @param order Order to set.
416 */
417static void zone_buddy_set_order(buddy_system_t *b, link_t *block,
418 uint8_t order)
419{
420 frame_t *frame;
421 frame = list_get_instance(block, frame_t, buddy_link);
422 frame->buddy_order = order;
423}
424
425/** Buddy system get_order implementation.
426 *
427 * @param b Buddy system.
428 * @param block Buddy system block.
429 *
430 * @return Order of block.
431 */
432static uint8_t zone_buddy_get_order(buddy_system_t *b, link_t *block)
433{
434 frame_t *frame;
435 frame = list_get_instance(block, frame_t, buddy_link);
436 return frame->buddy_order;
437}
438
439/** Buddy system mark_busy implementation.
440 *
441 * @param b Buddy system.
442 * @param block Buddy system block.
443 */
444static void zone_buddy_mark_busy(buddy_system_t *b, link_t * block)
445{
446 frame_t * frame;
447
448 frame = list_get_instance(block, frame_t, buddy_link);
449 frame->refcount = 1;
450}
451
452/** Buddy system mark_available implementation.
453 *
454 * @param b Buddy system.
455 * @param block Buddy system block.
456 */
457static void zone_buddy_mark_available(buddy_system_t *b, link_t *block)
458{
459 frame_t *frame;
460 frame = list_get_instance(block, frame_t, buddy_link);
461 frame->refcount = 0;
462}
463
464static buddy_system_operations_t zone_buddy_system_operations = {
465 .find_buddy = zone_buddy_find_buddy,
466 .bisect = zone_buddy_bisect,
467 .coalesce = zone_buddy_coalesce,
468 .set_order = zone_buddy_set_order,
469 .get_order = zone_buddy_get_order,
470 .mark_busy = zone_buddy_mark_busy,
471 .mark_available = zone_buddy_mark_available,
472 .find_block = zone_buddy_find_block,
473 .print_id = zone_buddy_print_id
474};
475
476/******************/
477/* Zone functions */
478/******************/
479
480/** Allocate frame in particular zone.
481 *
482 * Assume zone is locked.
483 * Panics if allocation is impossible.
484 *
485 * @param zone Zone to allocate from.
486 * @param order Allocate exactly 2^order frames.
487 *
488 * @return Frame index in zone.
489 *
490 */
491static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
492{
493 pfn_t v;
494 link_t *tmp;
495 frame_t *frame;
496
497 /* Allocate frames from zone buddy system */
498 tmp = buddy_system_alloc(zone->buddy_system, order);
499
500 ASSERT(tmp);
501
502 /* Update zone information. */
503 zone->free_count -= (1 << order);
504 zone->busy_count += (1 << order);
505
506 /* Frame will be actually a first frame of the block. */
507 frame = list_get_instance(tmp, frame_t, buddy_link);
508
509 /* get frame address */
510 v = make_frame_index(zone, frame);
511 return v;
512}
513
514/** Free frame from zone.
515 *
516 * Assume zone is locked.
517 *
518 * @param zone Pointer to zone from which the frame is to be freed.
519 * @param frame_idx Frame index relative to zone.
520 */
521static void zone_frame_free(zone_t *zone, index_t frame_idx)
522{
523 frame_t *frame;
524 uint8_t order;
525
526 frame = &zone->frames[frame_idx];
527
528 /* remember frame order */
529 order = frame->buddy_order;
530
531 ASSERT(frame->refcount);
532
533 if (!--frame->refcount) {
534 buddy_system_free(zone->buddy_system, &frame->buddy_link);
535
536 /* Update zone information. */
537 zone->free_count += (1 << order);
538 zone->busy_count -= (1 << order);
539 }
540}
541
542/** Return frame from zone. */
543static frame_t *zone_get_frame(zone_t *zone, index_t frame_idx)
544{
545 ASSERT(frame_idx < zone->count);
546 return &zone->frames[frame_idx];
547}
548
549/** Mark frame in zone unavailable to allocation. */
550static void zone_mark_unavailable(zone_t *zone, index_t frame_idx)
551{
552 frame_t *frame;
553 link_t *link;
554
555 frame = zone_get_frame(zone, frame_idx);
556 if (frame->refcount)
557 return;
558 link = buddy_system_alloc_block(zone->buddy_system,
559 &frame->buddy_link);
560 ASSERT(link);
561 zone->free_count--;
562
563 mutex_lock(&mem_avail_mtx);
564 mem_avail_frames--;
565 mutex_unlock(&mem_avail_mtx);
566}
567
568/** Join two zones.
569 *
570 * Expect zone_t *z to point to space at least zone_conf_size large.
571 *
572 * Assume z1 & z2 are locked.
573 *
574 * @param z Target zone structure pointer.
575 * @param z1 Zone to merge.
576 * @param z2 Zone to merge.
577 */
578static void _zone_merge(zone_t *z, zone_t *z1, zone_t *z2)
579{
580 uint8_t max_order;
581 unsigned int i;
582 int z2idx;
583 pfn_t frame_idx;
584 frame_t *frame;
585
586 ASSERT(!overlaps(z1->base, z1->count, z2->base, z2->count));
587 ASSERT(z1->base < z2->base);
588
589 spinlock_initialize(&z->lock, "zone_lock");
590 z->base = z1->base;
591 z->count = z2->base + z2->count - z1->base;
592 z->flags = z1->flags & z2->flags;
593
594 z->free_count = z1->free_count + z2->free_count;
595 z->busy_count = z1->busy_count + z2->busy_count;
596
597 max_order = fnzb(z->count);
598
599 z->buddy_system = (buddy_system_t *) &z[1];
600 buddy_system_create(z->buddy_system, max_order,
601 &zone_buddy_system_operations, (void *) z);
602
603 z->frames = (frame_t *)((uint8_t *) z->buddy_system +
604 buddy_conf_size(max_order));
605 for (i = 0; i < z->count; i++) {
606 /* This marks all frames busy */
607 frame_initialize(&z->frames[i]);
608 }
609 /* Copy frames from both zones to preserve full frame orders,
610 * parents etc. Set all free frames with refcount=0 to 1, because
611 * we add all free frames to buddy allocator later again, clear
612 * order to 0. Don't set busy frames with refcount=0, as they
613 * will not be reallocated during merge and it would make later
614 * problems with allocation/free.
615 */
616 for (i = 0; i < z1->count; i++)
617 z->frames[i] = z1->frames[i];
618 for (i = 0; i < z2->count; i++) {
619 z2idx = i + (z2->base - z1->base);
620 z->frames[z2idx] = z2->frames[i];
621 }
622 i = 0;
623 while (i < z->count) {
624 if (z->frames[i].refcount) {
625 /* skip busy frames */
626 i += 1 << z->frames[i].buddy_order;
627 } else { /* Free frames, set refcount=1 */
628 /* All free frames have refcount=0, we need not
629 * to check the order */
630 z->frames[i].refcount = 1;
631 z->frames[i].buddy_order = 0;
632 i++;
633 }
634 }
635 /* Add free blocks from the 2 original zones */
636 while (zone_can_alloc(z1, 0)) {
637 frame_idx = zone_frame_alloc(z1, 0);
638 frame = &z->frames[frame_idx];
639 frame->refcount = 0;
640 buddy_system_free(z->buddy_system, &frame->buddy_link);
641 }
642 while (zone_can_alloc(z2, 0)) {
643 frame_idx = zone_frame_alloc(z2, 0);
644 frame = &z->frames[frame_idx + (z2->base - z1->base)];
645 frame->refcount = 0;
646 buddy_system_free(z->buddy_system, &frame->buddy_link);
647 }
648}
649
650/** Return old configuration frames into the zone.
651 *
652 * We have several cases
653 * - the conf. data is outside of zone -> exit, shall we call frame_free??
654 * - the conf. data was created by zone_create or
655 * updated with reduce_region -> free every frame
656 *
657 * @param newzone The actual zone where freeing should occur.
658 * @param oldzone Pointer to old zone configuration data that should
659 * be freed from new zone.
660 */
661static void return_config_frames(zone_t *newzone, zone_t *oldzone)
662{
663 pfn_t pfn;
664 frame_t *frame;
665 count_t cframes;
666 unsigned int i;
667
668 pfn = ADDR2PFN((uintptr_t)KA2PA(oldzone));
669 cframes = SIZE2FRAMES(zone_conf_size(oldzone->count));
670
671 if (pfn < newzone->base || pfn >= newzone->base + newzone->count)
672 return;
673
674 frame = &newzone->frames[pfn - newzone->base];
675 ASSERT(!frame->buddy_order);
676
677 for (i = 0; i < cframes; i++) {
678 newzone->busy_count++;
679 zone_frame_free(newzone, pfn+i-newzone->base);
680 }
681}
682
683/** Reduce allocated block to count of order 0 frames.
684 *
685 * The allocated block need 2^order frames of space. Reduce all frames
686 * in block to order 0 and free the unneeded frames. This means, that
687 * when freeing the previously allocated block starting with frame_idx,
688 * you have to free every frame.
689 *
690 * @param zone
691 * @param frame_idx Index to block.
692 * @param count Allocated space in block.
693 */
694static void zone_reduce_region(zone_t *zone, pfn_t frame_idx, count_t count)
695{
696 count_t i;
697 uint8_t order;
698 frame_t *frame;
699
700 ASSERT(frame_idx + count < zone->count);
701
702 order = zone->frames[frame_idx].buddy_order;
703 ASSERT((count_t) (1 << order) >= count);
704
705 /* Reduce all blocks to order 0 */
706 for (i = 0; i < (count_t) (1 << order); i++) {
707 frame = &zone->frames[i + frame_idx];
708 frame->buddy_order = 0;
709 if (!frame->refcount)
710 frame->refcount = 1;
711 ASSERT(frame->refcount == 1);
712 }
713 /* Free unneeded frames */
714 for (i = count; i < (count_t) (1 << order); i++) {
715 zone_frame_free(zone, i + frame_idx);
716 }
717}
718
719/** Merge zones z1 and z2.
720 *
721 * - the zones must be 2 zones with no zone existing in between,
722 * which means that z2 = z1+1
723 *
724 * - When you create a new zone, the frame allocator configuration does
725 * not to be 2^order size. Once the allocator is running it is no longer
726 * possible, merged configuration data occupies more space :-/
727 */
728void zone_merge(unsigned int z1, unsigned int z2)
729{
730 ipl_t ipl;
731 zone_t *zone1, *zone2, *newzone;
732 unsigned int cframes;
733 uint8_t order;
734 unsigned int i;
735 pfn_t pfn;
736
737 ipl = interrupts_disable();
738 spinlock_lock(&zones.lock);
739
740 if ((z1 >= zones.count) || (z2 >= zones.count))
741 goto errout;
742 /* We can join only 2 zones with none existing inbetween */
743 if (z2 - z1 != 1)
744 goto errout;
745
746 zone1 = zones.info[z1];
747 zone2 = zones.info[z2];
748 spinlock_lock(&zone1->lock);
749 spinlock_lock(&zone2->lock);
750
751 cframes = SIZE2FRAMES(zone_conf_size(zone2->base + zone2->count -
752 zone1->base));
753 if (cframes == 1)
754 order = 0;
755 else
756 order = fnzb(cframes - 1) + 1;
757
758 /* Allocate zonedata inside one of the zones */
759 if (zone_can_alloc(zone1, order))
760 pfn = zone1->base + zone_frame_alloc(zone1, order);
761 else if (zone_can_alloc(zone2, order))
762 pfn = zone2->base + zone_frame_alloc(zone2, order);
763 else
764 goto errout2;
765
766 newzone = (zone_t *) PA2KA(PFN2ADDR(pfn));
767
768 _zone_merge(newzone, zone1, zone2);
769
770 /* Free unneeded config frames */
771 zone_reduce_region(newzone, pfn - newzone->base, cframes);
772 /* Subtract zone information from busy frames */
773 newzone->busy_count -= cframes;
774
775 /* Replace existing zones in zoneinfo list */
776 zones.info[z1] = newzone;
777 for (i = z2 + 1; i < zones.count; i++)
778 zones.info[i - 1] = zones.info[i];
779 zones.count--;
780
781 /* Free old zone information */
782 return_config_frames(newzone, zone1);
783 return_config_frames(newzone, zone2);
784errout2:
785 /* Nobody is allowed to enter to zone, so we are safe
786 * to touch the spinlocks last time */
787 spinlock_unlock(&zone1->lock);
788 spinlock_unlock(&zone2->lock);
789errout:
790 spinlock_unlock(&zones.lock);
791 interrupts_restore(ipl);
792}
793
794/** Merge all zones into one big zone.
795 *
796 * It is reasonable to do this on systems whose bios reports parts in chunks,
797 * so that we could have 1 zone (it's faster).
798 */
799void zone_merge_all(void)
800{
801 int count = zones.count;
802
803 while (zones.count > 1 && --count) {
804 zone_merge(0, 1);
805 break;
806 }
807}
808
809/** Create new frame zone.
810 *
811 * @param start Physical address of the first frame within the zone.
812 * @param count Count of frames in zone.
813 * @param z Address of configuration information of zone.
814 * @param flags Zone flags.
815 *
816 * @return Initialized zone.
817 */
818static void zone_construct(pfn_t start, count_t count, zone_t *z, int flags)
819{
820 unsigned int i;
821 uint8_t max_order;
822
823 spinlock_initialize(&z->lock, "zone_lock");
824 z->base = start;
825 z->count = count;
826 z->flags = flags;
827 z->free_count = count;
828 z->busy_count = 0;
829
830 /*
831 * Compute order for buddy system, initialize
832 */
833 max_order = fnzb(count);
834 z->buddy_system = (buddy_system_t *)&z[1];
835
836 buddy_system_create(z->buddy_system, max_order,
837 &zone_buddy_system_operations, (void *) z);
838
839 /* Allocate frames _after_ the conframe */
840 /* Check sizes */
841 z->frames = (frame_t *)((uint8_t *) z->buddy_system +
842 buddy_conf_size(max_order));
843 for (i = 0; i < count; i++) {
844 frame_initialize(&z->frames[i]);
845 }
846
847 /* Stuffing frames */
848 for (i = 0; i < count; i++) {
849 z->frames[i].refcount = 0;
850 buddy_system_free(z->buddy_system, &z->frames[i].buddy_link);
851 }
852}
853
854/** Compute configuration data size for zone.
855 *
856 * @param count Size of zone in frames.
857 * @return Size of zone configuration info (in bytes).
858 */
859uintptr_t zone_conf_size(count_t count)
860{
861 int size = sizeof(zone_t) + count * sizeof(frame_t);
862 int max_order;
863
864 max_order = fnzb(count);
865 size += buddy_conf_size(max_order);
866 return size;
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 */
884int zone_create(pfn_t start, count_t count, pfn_t confframe, int flags)
885{
886 zone_t *z;
887 uintptr_t addr;
888 count_t confcount;
889 unsigned int i;
890 int znum;
891
892 /* Theoretically we could have here 0, practically make sure
893 * nobody tries to do that. If some platform requires, remove
894 * the assert
895 */
896 ASSERT(confframe);
897 /* If conframe is supposed to be inside our zone, then make sure
898 * it does not span kernel & init
899 */
900 confcount = SIZE2FRAMES(zone_conf_size(count));
901 if (confframe >= start && confframe < start + count) {
902 for (; confframe < start + count; confframe++) {
903 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 count_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 if (confframe >= start + count)
927 panic("Cannot find configuration data for zone.");
928 }
929
930 z = (zone_t *) PA2KA(PFN2ADDR(confframe));
931 zone_construct(start, count, z, flags);
932 znum = zones_add_zone(z);
933 if (znum == -1)
934 return -1;
935
936 mutex_lock(&mem_avail_mtx);
937 mem_avail_frames += count;
938 mutex_unlock(&mem_avail_mtx);
939
940 /* If confdata in zone, mark as unavailable */
941 if (confframe >= start && confframe < start + count)
942 for (i = confframe; i < confframe + confcount; i++) {
943 zone_mark_unavailable(z, i - z->base);
944 }
945
946 return znum;
947}
948
949/***************************************/
950/* Frame functions */
951
952/** Set parent of frame. */
953void frame_set_parent(pfn_t pfn, void *data, unsigned int hint)
954{
955 zone_t *zone = find_zone_and_lock(pfn, &hint);
956
957 ASSERT(zone);
958
959 zone_get_frame(zone, pfn - zone->base)->parent = data;
960 spinlock_unlock(&zone->lock);
961}
962
963void *frame_get_parent(pfn_t pfn, unsigned int hint)
964{
965 zone_t *zone = find_zone_and_lock(pfn, &hint);
966 void *res;
967
968 ASSERT(zone);
969 res = zone_get_frame(zone, pfn - zone->base)->parent;
970
971 spinlock_unlock(&zone->lock);
972 return res;
973}
974
975/** Allocate power-of-two frames of physical memory.
976 *
977 * @param order Allocate exactly 2^order frames.
978 * @param flags Flags for host zone selection and address processing.
979 * @param pzone Preferred zone.
980 *
981 * @return Physical address of the allocated frame.
982 *
983 */
984void *frame_alloc_generic(uint8_t order, int flags, unsigned int *pzone)
985{
986 ipl_t ipl;
987 int freed;
988 pfn_t v;
989 zone_t *zone;
990 unsigned long gen = 0;
991
992loop:
993 ipl = interrupts_disable();
994
995 /*
996 * First, find suitable frame zone.
997 */
998 zone = find_free_zone_and_lock(order, pzone);
999
1000 /* If no memory, reclaim some slab memory,
1001 if it does not help, reclaim all */
1002 if (!zone && !(flags & FRAME_NO_RECLAIM)) {
1003 freed = slab_reclaim(0);
1004 if (freed)
1005 zone = find_free_zone_and_lock(order, pzone);
1006 if (!zone) {
1007 freed = slab_reclaim(SLAB_RECLAIM_ALL);
1008 if (freed)
1009 zone = find_free_zone_and_lock(order, pzone);
1010 }
1011 }
1012 if (!zone) {
1013 /*
1014 * Sleep until some frames are available again.
1015 */
1016 if (flags & FRAME_ATOMIC) {
1017 interrupts_restore(ipl);
1018 return 0;
1019 }
1020
1021#ifdef CONFIG_DEBUG
1022 unsigned long avail;
1023
1024 mutex_lock(&mem_avail_mtx);
1025 avail = mem_avail_frames;
1026 mutex_unlock(&mem_avail_mtx);
1027
1028 printf("Thread %" PRIu64 " waiting for %u frames, "
1029 "%u available.\n", THREAD->tid, 1ULL << order, avail);
1030#endif
1031
1032 mutex_lock(&mem_avail_mtx);
1033 while ((mem_avail_frames < (1ULL << order)) ||
1034 gen == mem_avail_gen)
1035 condvar_wait(&mem_avail_cv, &mem_avail_mtx);
1036 gen = mem_avail_gen;
1037 mutex_unlock(&mem_avail_mtx);
1038
1039#ifdef CONFIG_DEBUG
1040 mutex_lock(&mem_avail_mtx);
1041 avail = mem_avail_frames;
1042 mutex_unlock(&mem_avail_mtx);
1043
1044 printf("Thread %" PRIu64 " woken up, %u frames available.\n",
1045 THREAD->tid, avail);
1046#endif
1047
1048 interrupts_restore(ipl);
1049 goto loop;
1050 }
1051
1052 v = zone_frame_alloc(zone, order);
1053 v += zone->base;
1054
1055 spinlock_unlock(&zone->lock);
1056
1057 mutex_lock(&mem_avail_mtx);
1058 mem_avail_frames -= (1ULL << order);
1059 mutex_unlock(&mem_avail_mtx);
1060
1061 interrupts_restore(ipl);
1062
1063 if (flags & FRAME_KA)
1064 return (void *)PA2KA(PFN2ADDR(v));
1065 return (void *)PFN2ADDR(v);
1066}
1067
1068/** Free a frame.
1069 *
1070 * Find respective frame structure for supplied physical frame address.
1071 * Decrement frame reference count.
1072 * If it drops to zero, move the frame structure to free list.
1073 *
1074 * @param frame Physical Address of of the frame to be freed.
1075 */
1076void frame_free(uintptr_t frame)
1077{
1078 ipl_t ipl;
1079 zone_t *zone;
1080 pfn_t pfn = ADDR2PFN(frame);
1081
1082 ipl = interrupts_disable();
1083
1084 /*
1085 * First, find host frame zone for addr.
1086 */
1087 zone = find_zone_and_lock(pfn, NULL);
1088 ASSERT(zone);
1089
1090 zone_frame_free(zone, pfn - zone->base);
1091
1092 spinlock_unlock(&zone->lock);
1093
1094 /*
1095 * Signal that some memory has been freed.
1096 */
1097 mutex_lock(&mem_avail_mtx);
1098 mem_avail_frames++;
1099 mem_avail_gen++;
1100 condvar_broadcast(&mem_avail_cv);
1101 mutex_unlock(&mem_avail_mtx);
1102
1103 interrupts_restore(ipl);
1104}
1105
1106/** Add reference to frame.
1107 *
1108 * Find respective frame structure for supplied PFN and
1109 * increment frame reference count.
1110 *
1111 * @param pfn Frame number of the frame to be freed.
1112 */
1113void frame_reference_add(pfn_t pfn)
1114{
1115 ipl_t ipl;
1116 zone_t *zone;
1117 frame_t *frame;
1118
1119 ipl = interrupts_disable();
1120
1121 /*
1122 * First, find host frame zone for addr.
1123 */
1124 zone = find_zone_and_lock(pfn, NULL);
1125 ASSERT(zone);
1126
1127 frame = &zone->frames[pfn - zone->base];
1128 frame->refcount++;
1129
1130 spinlock_unlock(&zone->lock);
1131 interrupts_restore(ipl);
1132}
1133
1134/** Mark given range unavailable in frame zones. */
1135void frame_mark_unavailable(pfn_t start, count_t count)
1136{
1137 unsigned int i;
1138 zone_t *zone;
1139 unsigned int prefzone = 0;
1140
1141 for (i = 0; i < count; i++) {
1142 zone = find_zone_and_lock(start + i, &prefzone);
1143 if (!zone) /* PFN not found */
1144 continue;
1145 zone_mark_unavailable(zone, start + i - zone->base);
1146
1147 spinlock_unlock(&zone->lock);
1148 }
1149}
1150
1151/** Initialize physical memory management. */
1152void frame_init(void)
1153{
1154 if (config.cpu_active == 1) {
1155 zones.count = 0;
1156 spinlock_initialize(&zones.lock, "zones.lock");
1157 mutex_initialize(&mem_avail_mtx, MUTEX_ACTIVE);
1158 condvar_initialize(&mem_avail_cv);
1159 }
1160 /* Tell the architecture to create some memory */
1161 frame_arch_init();
1162 if (config.cpu_active == 1) {
1163 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1164 SIZE2FRAMES(config.kernel_size));
1165 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
1166 SIZE2FRAMES(config.stack_size));
1167
1168 count_t i;
1169 for (i = 0; i < init.cnt; i++) {
1170 pfn_t pfn = ADDR2PFN(KA2PA(init.tasks[i].addr));
1171 frame_mark_unavailable(pfn,
1172 SIZE2FRAMES(init.tasks[i].size));
1173 }
1174
1175 if (ballocs.size)
1176 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1177 SIZE2FRAMES(ballocs.size));
1178
1179 /* Black list first frame, as allocating NULL would
1180 * fail in some places */
1181 frame_mark_unavailable(0, 1);
1182 }
1183}
1184
1185
1186/** Return total size of all zones. */
1187uint64_t zone_total_size(void)
1188{
1189 zone_t *zone = NULL;
1190 unsigned int i;
1191 ipl_t ipl;
1192 uint64_t total = 0;
1193
1194 ipl = interrupts_disable();
1195 spinlock_lock(&zones.lock);
1196
1197 for (i = 0; i < zones.count; i++) {
1198 zone = zones.info[i];
1199 spinlock_lock(&zone->lock);
1200 total += (uint64_t) FRAMES2SIZE(zone->count);
1201 spinlock_unlock(&zone->lock);
1202 }
1203
1204 spinlock_unlock(&zones.lock);
1205 interrupts_restore(ipl);
1206
1207 return total;
1208}
1209
1210/** Prints list of zones. */
1211void zone_print_list(void)
1212{
1213 zone_t *zone = NULL;
1214 unsigned int i;
1215 ipl_t ipl;
1216
1217 ipl = interrupts_disable();
1218 spinlock_lock(&zones.lock);
1219
1220#ifdef __32_BITS__
1221 printf("# base address free frames busy frames\n");
1222 printf("-- ------------ ------------ ------------\n");
1223#endif
1224
1225#ifdef __64_BITS__
1226 printf("# base address free frames busy frames\n");
1227 printf("-- -------------------- ------------ ------------\n");
1228#endif
1229
1230 for (i = 0; i < zones.count; i++) {
1231 zone = zones.info[i];
1232 spinlock_lock(&zone->lock);
1233
1234#ifdef __32_BITS__
1235 printf("%-2u %10p %12" PRIc " %12" PRIc "\n",
1236 i, PFN2ADDR(zone->base), zone->free_count,
1237 zone->busy_count);
1238#endif
1239
1240#ifdef __64_BITS__
1241 printf("%-2u %18p %12" PRIc " %12" PRIc "\n", i,
1242 PFN2ADDR(zone->base), zone->free_count, zone->busy_count);
1243#endif
1244
1245 spinlock_unlock(&zone->lock);
1246 }
1247
1248 spinlock_unlock(&zones.lock);
1249 interrupts_restore(ipl);
1250}
1251
1252/** Prints zone details.
1253 *
1254 * @param num Zone base address or zone number.
1255 */
1256void zone_print_one(unsigned int num)
1257{
1258 zone_t *zone = NULL;
1259 ipl_t ipl;
1260 unsigned int i;
1261
1262 ipl = interrupts_disable();
1263 spinlock_lock(&zones.lock);
1264
1265 for (i = 0; i < zones.count; i++) {
1266 if ((i == num) || (PFN2ADDR(zones.info[i]->base) == num)) {
1267 zone = zones.info[i];
1268 break;
1269 }
1270 }
1271 if (!zone) {
1272 printf("Zone not found.\n");
1273 goto out;
1274 }
1275
1276 spinlock_lock(&zone->lock);
1277 printf("Memory zone information\n");
1278 printf("Zone base address: %p\n", PFN2ADDR(zone->base));
1279 printf("Zone size: %" PRIc " frames (%" PRIs " KB)\n", zone->count,
1280 SIZE2KB(FRAMES2SIZE(zone->count)));
1281 printf("Allocated space: %" PRIc " frames (%" PRIs " KB)\n",
1282 zone->busy_count, SIZE2KB(FRAMES2SIZE(zone->busy_count)));
1283 printf("Available space: %" PRIc " frames (%" PRIs " KB)\n",
1284 zone->free_count, SIZE2KB(FRAMES2SIZE(zone->free_count)));
1285 buddy_system_structure_print(zone->buddy_system, FRAME_SIZE);
1286 spinlock_unlock(&zone->lock);
1287
1288out:
1289 spinlock_unlock(&zones.lock);
1290 interrupts_restore(ipl);
1291}
1292
1293/** @}
1294 */
1295
Note: See TracBrowser for help on using the repository browser.