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

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

Get rid of the infamous "Sleep not implemented" panic during low memory
condition. Use a condition variable to implement the sleep. As of now, the
condition is rather coarse and can cause that threads may sleep unnecessarily.
Needs to be fine tuned and based on the amount of memory available.

  • Property mode set to 100644
File size: 30.8 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 zones_mtx;
113condvar_t zones_cv;
114int new_freed_mem = false;
115
116/********************/
117/* Helper functions */
118/********************/
119
120static inline index_t frame_index(zone_t *zone, frame_t *frame)
121{
122 return (index_t) (frame - zone->frames);
123}
124
125static inline index_t frame_index_abs(zone_t *zone, frame_t *frame)
126{
127 return (index_t) (frame - zone->frames) + zone->base;
128}
129
130static inline int frame_index_valid(zone_t *zone, index_t index)
131{
132 return (index < zone->count);
133}
134
135/** Compute pfn_t from frame_t pointer & zone pointer */
136static index_t make_frame_index(zone_t *zone, frame_t *frame)
137{
138 return (frame - zone->frames);
139}
140
141/** Initialize frame structure.
142 *
143 * @param framei Frame structure to be initialized.
144 */
145static void frame_initialize(frame_t *frame)
146{
147 frame->refcount = 1;
148 frame->buddy_order = 0;
149}
150
151/**********************/
152/* Zoneinfo functions */
153/**********************/
154
155/** Insert-sort zone into zones list.
156 *
157 * @param newzone New zone to be inserted into zone list.
158 * @return Zone number on success, -1 on error.
159 */
160static int zones_add_zone(zone_t *newzone)
161{
162 unsigned int i, j;
163 ipl_t ipl;
164 zone_t *z;
165
166 ipl = interrupts_disable();
167 spinlock_lock(&zones.lock);
168
169 /* Try to merge */
170 if (zones.count + 1 == ZONES_MAX) {
171 printf("Maximum zone count %u exceeded!\n", ZONES_MAX);
172 spinlock_unlock(&zones.lock);
173 interrupts_restore(ipl);
174 return -1;
175 }
176
177 for (i = 0; i < zones.count; i++) {
178 /* Check for overflow */
179 z = zones.info[i];
180 if (overlaps(newzone->base, newzone->count, z->base,
181 z->count)) {
182 printf("Zones overlap!\n");
183 return -1;
184 }
185 if (newzone->base < z->base)
186 break;
187 }
188
189 /* Move other zones up */
190 for (j = i; j < zones.count; j++)
191 zones.info[j + 1] = zones.info[j];
192
193 zones.info[i] = newzone;
194 zones.count++;
195
196 spinlock_unlock(&zones.lock);
197 interrupts_restore(ipl);
198
199 return i;
200}
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 pzone Pointer to preferred zone or NULL, on return contains
257 * zone number.
258 */
259static zone_t *find_free_zone_and_lock(uint8_t order, unsigned int *pzone)
260{
261 unsigned int i;
262 zone_t *z;
263 unsigned int hint = pzone ? *pzone : 0;
264
265 spinlock_lock(&zones.lock);
266 if (hint >= zones.count)
267 hint = 0;
268 i = hint;
269 do {
270 z = zones.info[i];
271
272 spinlock_lock(&z->lock);
273
274 /* Check if the zone has 2^order frames area available */
275 if (zone_can_alloc(z, order)) {
276 spinlock_unlock(&zones.lock);
277 if (pzone)
278 *pzone = i;
279 return z;
280 }
281 spinlock_unlock(&z->lock);
282 if (++i >= zones.count)
283 i = 0;
284 } while(i != hint);
285 spinlock_unlock(&zones.lock);
286 return NULL;
287}
288
289/**************************/
290/* Buddy system functions */
291/**************************/
292
293/** Buddy system find_block implementation.
294 *
295 * Find block that is parent of current list.
296 * That means go to lower addresses, until such block is found
297 *
298 * @param order Order of parent must be different then this
299 * parameter!!
300 */
301static link_t *zone_buddy_find_block(buddy_system_t *b, link_t *child,
302 uint8_t order)
303{
304 frame_t *frame;
305 zone_t *zone;
306 index_t index;
307
308 frame = list_get_instance(child, frame_t, buddy_link);
309 zone = (zone_t *) b->data;
310
311 index = frame_index(zone, frame);
312 do {
313 if (zone->frames[index].buddy_order != order) {
314 return &zone->frames[index].buddy_link;
315 }
316 } while(index-- > 0);
317 return NULL;
318}
319
320static void zone_buddy_print_id(buddy_system_t *b, link_t *block)
321{
322 frame_t *frame;
323 zone_t *zone;
324 index_t index;
325
326 frame = list_get_instance(block, frame_t, buddy_link);
327 zone = (zone_t *) b->data;
328 index = frame_index(zone, frame);
329 printf("%" PRIi, index);
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 .print_id = zone_buddy_print_id
473};
474
475/******************/
476/* Zone functions */
477/******************/
478
479/** Allocate frame in particular zone.
480 *
481 * Assume zone is locked.
482 * Panics if allocation is impossible.
483 *
484 * @param zone Zone to allocate from.
485 * @param order Allocate exactly 2^order frames.
486 *
487 * @return Frame index in zone.
488 *
489 */
490static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
491{
492 pfn_t v;
493 link_t *tmp;
494 frame_t *frame;
495
496 /* Allocate frames from zone buddy system */
497 tmp = buddy_system_alloc(zone->buddy_system, order);
498
499 ASSERT(tmp);
500
501 /* Update zone information. */
502 zone->free_count -= (1 << order);
503 zone->busy_count += (1 << order);
504
505 /* Frame will be actually a first frame of the block. */
506 frame = list_get_instance(tmp, frame_t, buddy_link);
507
508 /* get frame address */
509 v = make_frame_index(zone, frame);
510 return v;
511}
512
513/** Free frame from zone.
514 *
515 * Assume zone is locked.
516 *
517 * @param zone Pointer to zone from which the frame is to be freed.
518 * @param frame_idx Frame index relative to zone.
519 */
520static void zone_frame_free(zone_t *zone, index_t frame_idx)
521{
522 frame_t *frame;
523 uint8_t order;
524
525 frame = &zone->frames[frame_idx];
526
527 /* remember frame order */
528 order = frame->buddy_order;
529
530 ASSERT(frame->refcount);
531
532 if (!--frame->refcount) {
533 buddy_system_free(zone->buddy_system, &frame->buddy_link);
534
535 /* Update zone information. */
536 zone->free_count += (1 << order);
537 zone->busy_count -= (1 << order);
538 }
539}
540
541/** Return frame from zone. */
542static frame_t * zone_get_frame(zone_t *zone, index_t frame_idx)
543{
544 ASSERT(frame_idx < zone->count);
545 return &zone->frames[frame_idx];
546}
547
548/** Mark frame in zone unavailable to allocation. */
549static void zone_mark_unavailable(zone_t *zone, index_t frame_idx)
550{
551 frame_t *frame;
552 link_t *link;
553
554 frame = zone_get_frame(zone, frame_idx);
555 if (frame->refcount)
556 return;
557 link = buddy_system_alloc_block(zone->buddy_system,
558 &frame->buddy_link);
559 ASSERT(link);
560 zone->free_count--;
561}
562
563/** Join two zones.
564 *
565 * Expect zone_t *z to point to space at least zone_conf_size large.
566 *
567 * Assume z1 & z2 are locked.
568 *
569 * @param z Target zone structure pointer.
570 * @param z1 Zone to merge.
571 * @param z2 Zone to merge.
572 */
573static void _zone_merge(zone_t *z, zone_t *z1, zone_t *z2)
574{
575 uint8_t max_order;
576 unsigned int i;
577 int z2idx;
578 pfn_t frame_idx;
579 frame_t *frame;
580
581 ASSERT(!overlaps(z1->base, z1->count, z2->base, z2->count));
582 ASSERT(z1->base < z2->base);
583
584 spinlock_initialize(&z->lock, "zone_lock");
585 z->base = z1->base;
586 z->count = z2->base + z2->count - z1->base;
587 z->flags = z1->flags & z2->flags;
588
589 z->free_count = z1->free_count + z2->free_count;
590 z->busy_count = z1->busy_count + z2->busy_count;
591
592 max_order = fnzb(z->count);
593
594 z->buddy_system = (buddy_system_t *) &z[1];
595 buddy_system_create(z->buddy_system, max_order,
596 &zone_buddy_system_operations, (void *) z);
597
598 z->frames = (frame_t *)((uint8_t *) z->buddy_system +
599 buddy_conf_size(max_order));
600 for (i = 0; i < z->count; i++) {
601 /* This marks all frames busy */
602 frame_initialize(&z->frames[i]);
603 }
604 /* Copy frames from both zones to preserve full frame orders,
605 * parents etc. Set all free frames with refcount=0 to 1, because
606 * we add all free frames to buddy allocator later again, clear
607 * order to 0. Don't set busy frames with refcount=0, as they
608 * will not be reallocated during merge and it would make later
609 * problems with allocation/free.
610 */
611 for (i = 0; i < z1->count; i++)
612 z->frames[i] = z1->frames[i];
613 for (i = 0; i < z2->count; i++) {
614 z2idx = i + (z2->base - z1->base);
615 z->frames[z2idx] = z2->frames[i];
616 }
617 i = 0;
618 while (i < z->count) {
619 if (z->frames[i].refcount) {
620 /* skip busy frames */
621 i += 1 << z->frames[i].buddy_order;
622 } else { /* Free frames, set refcount=1 */
623 /* All free frames have refcount=0, we need not
624 * to check the order */
625 z->frames[i].refcount = 1;
626 z->frames[i].buddy_order = 0;
627 i++;
628 }
629 }
630 /* Add free blocks from the 2 original zones */
631 while (zone_can_alloc(z1, 0)) {
632 frame_idx = zone_frame_alloc(z1, 0);
633 frame = &z->frames[frame_idx];
634 frame->refcount = 0;
635 buddy_system_free(z->buddy_system, &frame->buddy_link);
636 }
637 while (zone_can_alloc(z2, 0)) {
638 frame_idx = zone_frame_alloc(z2, 0);
639 frame = &z->frames[frame_idx + (z2->base - z1->base)];
640 frame->refcount = 0;
641 buddy_system_free(z->buddy_system, &frame->buddy_link);
642 }
643}
644
645/** Return old configuration frames into the zone.
646 *
647 * We have several cases
648 * - the conf. data is outside of zone -> exit, shall we call frame_free??
649 * - the conf. data was created by zone_create or
650 * updated with reduce_region -> free every frame
651 *
652 * @param newzone The actual zone where freeing should occur.
653 * @param oldzone Pointer to old zone configuration data that should
654 * be freed from new zone.
655 */
656static void return_config_frames(zone_t *newzone, zone_t *oldzone)
657{
658 pfn_t pfn;
659 frame_t *frame;
660 count_t cframes;
661 unsigned int i;
662
663 pfn = ADDR2PFN((uintptr_t)KA2PA(oldzone));
664 cframes = SIZE2FRAMES(zone_conf_size(oldzone->count));
665
666 if (pfn < newzone->base || pfn >= newzone->base + newzone->count)
667 return;
668
669 frame = &newzone->frames[pfn - newzone->base];
670 ASSERT(!frame->buddy_order);
671
672 for (i = 0; i < cframes; i++) {
673 newzone->busy_count++;
674 zone_frame_free(newzone, pfn+i-newzone->base);
675 }
676}
677
678/** Reduce allocated block to count of order 0 frames.
679 *
680 * The allocated block need 2^order frames of space. Reduce all frames
681 * in block to order 0 and free the unneeded frames. This means, that
682 * when freeing the previously allocated block starting with frame_idx,
683 * you have to free every frame.
684 *
685 * @param zone
686 * @param frame_idx Index to block.
687 * @param count Allocated space in block.
688 */
689static void zone_reduce_region(zone_t *zone, pfn_t frame_idx, count_t count)
690{
691 count_t i;
692 uint8_t order;
693 frame_t *frame;
694
695 ASSERT(frame_idx + count < zone->count);
696
697 order = zone->frames[frame_idx].buddy_order;
698 ASSERT((count_t) (1 << order) >= count);
699
700 /* Reduce all blocks to order 0 */
701 for (i = 0; i < (count_t) (1 << order); i++) {
702 frame = &zone->frames[i + frame_idx];
703 frame->buddy_order = 0;
704 if (!frame->refcount)
705 frame->refcount = 1;
706 ASSERT(frame->refcount == 1);
707 }
708 /* Free unneeded frames */
709 for (i = count; i < (count_t) (1 << order); i++) {
710 zone_frame_free(zone, i + frame_idx);
711 }
712}
713
714/** Merge zones z1 and z2.
715 *
716 * - the zones must be 2 zones with no zone existing in between,
717 * which means that z2 = z1+1
718 *
719 * - When you create a new zone, the frame allocator configuration does
720 * not to be 2^order size. Once the allocator is running it is no longer
721 * possible, merged configuration data occupies more space :-/
722 */
723void zone_merge(unsigned int z1, unsigned int z2)
724{
725 ipl_t ipl;
726 zone_t *zone1, *zone2, *newzone;
727 unsigned int cframes;
728 uint8_t order;
729 unsigned int i;
730 pfn_t pfn;
731
732 ipl = interrupts_disable();
733 spinlock_lock(&zones.lock);
734
735 if ((z1 >= zones.count) || (z2 >= zones.count))
736 goto errout;
737 /* We can join only 2 zones with none existing inbetween */
738 if (z2-z1 != 1)
739 goto errout;
740
741 zone1 = zones.info[z1];
742 zone2 = zones.info[z2];
743 spinlock_lock(&zone1->lock);
744 spinlock_lock(&zone2->lock);
745
746 cframes = SIZE2FRAMES(zone_conf_size(zone2->base + zone2->count -
747 zone1->base));
748 if (cframes == 1)
749 order = 0;
750 else
751 order = fnzb(cframes - 1) + 1;
752
753 /* Allocate zonedata inside one of the zones */
754 if (zone_can_alloc(zone1, order))
755 pfn = zone1->base + zone_frame_alloc(zone1, order);
756 else if (zone_can_alloc(zone2, order))
757 pfn = zone2->base + zone_frame_alloc(zone2, order);
758 else
759 goto errout2;
760
761 newzone = (zone_t *) PA2KA(PFN2ADDR(pfn));
762
763 _zone_merge(newzone, zone1, zone2);
764
765 /* Free unneeded config frames */
766 zone_reduce_region(newzone, pfn - newzone->base, cframes);
767 /* Subtract zone information from busy frames */
768 newzone->busy_count -= cframes;
769
770 /* Replace existing zones in zoneinfo list */
771 zones.info[z1] = newzone;
772 for (i = z2 + 1; i < zones.count; i++)
773 zones.info[i - 1] = zones.info[i];
774 zones.count--;
775
776 /* Free old zone information */
777 return_config_frames(newzone, zone1);
778 return_config_frames(newzone, zone2);
779errout2:
780 /* Nobody is allowed to enter to zone, so we are safe
781 * to touch the spinlocks last time */
782 spinlock_unlock(&zone1->lock);
783 spinlock_unlock(&zone2->lock);
784errout:
785 spinlock_unlock(&zones.lock);
786 interrupts_restore(ipl);
787}
788
789/** Merge all zones into one big zone.
790 *
791 * It is reasonable to do this on systems whose bios reports parts in chunks,
792 * so that we could have 1 zone (it's faster).
793 */
794void zone_merge_all(void)
795{
796 int count = zones.count;
797
798 while (zones.count > 1 && --count) {
799 zone_merge(0,1);
800 break;
801 }
802}
803
804/** Create new frame zone.
805 *
806 * @param start Physical address of the first frame within the zone.
807 * @param count Count of frames in zone.
808 * @param z Address of configuration information of zone.
809 * @param flags Zone flags.
810 *
811 * @return Initialized zone.
812 */
813static void zone_construct(pfn_t start, count_t count, zone_t *z, int flags)
814{
815 unsigned int i;
816 uint8_t max_order;
817
818 spinlock_initialize(&z->lock, "zone_lock");
819 z->base = start;
820 z->count = count;
821 z->flags = flags;
822 z->free_count = count;
823 z->busy_count = 0;
824
825 /*
826 * Compute order for buddy system, initialize
827 */
828 max_order = fnzb(count);
829 z->buddy_system = (buddy_system_t *)&z[1];
830
831 buddy_system_create(z->buddy_system, max_order,
832 &zone_buddy_system_operations, (void *) z);
833
834 /* Allocate frames _after_ the conframe */
835 /* Check sizes */
836 z->frames = (frame_t *)((uint8_t *) z->buddy_system +
837 buddy_conf_size(max_order));
838 for (i = 0; i < count; i++) {
839 frame_initialize(&z->frames[i]);
840 }
841
842 /* Stuffing frames */
843 for (i = 0; i < count; i++) {
844 z->frames[i].refcount = 0;
845 buddy_system_free(z->buddy_system, &z->frames[i].buddy_link);
846 }
847}
848
849/** Compute configuration data size for zone.
850 *
851 * @param count Size of zone in frames.
852 * @return Size of zone configuration info (in bytes).
853 */
854uintptr_t zone_conf_size(count_t count)
855{
856 int size = sizeof(zone_t) + count * sizeof(frame_t);
857 int max_order;
858
859 max_order = fnzb(count);
860 size += buddy_conf_size(max_order);
861 return size;
862}
863
864/** Create and add zone to system.
865 *
866 * @param start First frame number (absolute).
867 * @param count Size of zone in frames.
868 * @param confframe Where configuration frames are supposed to be.
869 * Automatically checks, that we will not disturb the
870 * kernel and possibly init. If confframe is given
871 * _outside_ this zone, it is expected, that the area is
872 * already marked BUSY and big enough to contain
873 * zone_conf_size() amount of data. If the confframe is
874 * inside the area, the zone free frame information is
875 * modified not to include it.
876 *
877 * @return Zone number or -1 on error.
878 */
879int zone_create(pfn_t start, count_t count, pfn_t confframe, int flags)
880{
881 zone_t *z;
882 uintptr_t addr;
883 count_t confcount;
884 unsigned int i;
885 int znum;
886
887 /* Theoretically we could have here 0, practically make sure
888 * nobody tries to do that. If some platform requires, remove
889 * the assert
890 */
891 ASSERT(confframe);
892 /* If conframe is supposed to be inside our zone, then make sure
893 * it does not span kernel & init
894 */
895 confcount = SIZE2FRAMES(zone_conf_size(count));
896 if (confframe >= start && confframe < start+count) {
897 for (;confframe < start + count; confframe++) {
898 addr = PFN2ADDR(confframe);
899 if (overlaps(addr, PFN2ADDR(confcount),
900 KA2PA(config.base), config.kernel_size))
901 continue;
902
903 if (overlaps(addr, PFN2ADDR(confcount),
904 KA2PA(config.stack_base), config.stack_size))
905 continue;
906
907 bool overlap = false;
908 count_t i;
909 for (i = 0; i < init.cnt; i++)
910 if (overlaps(addr, PFN2ADDR(confcount),
911 KA2PA(init.tasks[i].addr),
912 init.tasks[i].size)) {
913 overlap = true;
914 break;
915 }
916 if (overlap)
917 continue;
918
919 break;
920 }
921 if (confframe >= start + count)
922 panic("Cannot find configuration data for zone.");
923 }
924
925 z = (zone_t *) PA2KA(PFN2ADDR(confframe));
926 zone_construct(start, count, z, flags);
927 znum = zones_add_zone(z);
928 if (znum == -1)
929 return -1;
930
931 /* If confdata in zone, mark as unavailable */
932 if (confframe >= start && confframe < start + count)
933 for (i = confframe; i < confframe + confcount; i++) {
934 zone_mark_unavailable(z, i - z->base);
935 }
936 return znum;
937}
938
939/***************************************/
940/* Frame functions */
941
942/** Set parent of frame. */
943void frame_set_parent(pfn_t pfn, void *data, unsigned int hint)
944{
945 zone_t *zone = find_zone_and_lock(pfn, &hint);
946
947 ASSERT(zone);
948
949 zone_get_frame(zone, pfn-zone->base)->parent = data;
950 spinlock_unlock(&zone->lock);
951}
952
953void *frame_get_parent(pfn_t pfn, unsigned int hint)
954{
955 zone_t *zone = find_zone_and_lock(pfn, &hint);
956 void *res;
957
958 ASSERT(zone);
959 res = zone_get_frame(zone, pfn - zone->base)->parent;
960
961 spinlock_unlock(&zone->lock);
962 return res;
963}
964
965/** Allocate power-of-two frames of physical memory.
966 *
967 * @param order Allocate exactly 2^order frames.
968 * @param flags Flags for host zone selection and address processing.
969 * @param pzone Preferred zone.
970 *
971 * @return Physical address of the allocated frame.
972 *
973 */
974void *frame_alloc_generic(uint8_t order, int flags, unsigned int *pzone)
975{
976 ipl_t ipl;
977 int freed;
978 pfn_t v;
979 zone_t *zone;
980
981loop:
982 ipl = interrupts_disable();
983
984 /*
985 * First, find suitable frame zone.
986 */
987 zone = find_free_zone_and_lock(order, pzone);
988
989 /* If no memory, reclaim some slab memory,
990 if it does not help, reclaim all */
991 if (!zone && !(flags & FRAME_NO_RECLAIM)) {
992 freed = slab_reclaim(0);
993 if (freed)
994 zone = find_free_zone_and_lock(order, pzone);
995 if (!zone) {
996 freed = slab_reclaim(SLAB_RECLAIM_ALL);
997 if (freed)
998 zone = find_free_zone_and_lock(order, pzone);
999 }
1000 }
1001 if (!zone) {
1002 /*
1003 * Sleep until some frames are available again.
1004 */
1005 if (flags & FRAME_ATOMIC) {
1006 interrupts_restore(ipl);
1007 return 0;
1008 }
1009
1010#ifdef CONFIG_DEBUG
1011 printf("Thread %" PRIu64 " falling asleep, low memory.\n", THREAD->tid);
1012#endif
1013
1014 mutex_lock(&zones_mtx);
1015 if (!new_freed_mem)
1016 condvar_wait(&zones_cv, &zones_mtx);
1017 new_freed_mem = false;
1018 mutex_unlock(&zones_mtx);
1019
1020#ifdef CONFIG_DEBUG
1021 printf("Thread %" PRIu64 " woken up, memory available.\n", THREAD->tid);
1022#endif
1023
1024 interrupts_restore(ipl);
1025 goto loop;
1026 }
1027
1028 v = zone_frame_alloc(zone, order);
1029 v += zone->base;
1030
1031 spinlock_unlock(&zone->lock);
1032 interrupts_restore(ipl);
1033
1034 if (flags & FRAME_KA)
1035 return (void *)PA2KA(PFN2ADDR(v));
1036 return (void *)PFN2ADDR(v);
1037}
1038
1039/** Free a frame.
1040 *
1041 * Find respective frame structure for supplied physical frame address.
1042 * Decrement frame reference count.
1043 * If it drops to zero, move the frame structure to free list.
1044 *
1045 * @param frame Physical Address of of the frame to be freed.
1046 */
1047void frame_free(uintptr_t frame)
1048{
1049 ipl_t ipl;
1050 zone_t *zone;
1051 pfn_t pfn = ADDR2PFN(frame);
1052
1053 ipl = interrupts_disable();
1054
1055 /*
1056 * First, find host frame zone for addr.
1057 */
1058 zone = find_zone_and_lock(pfn, NULL);
1059 ASSERT(zone);
1060
1061 zone_frame_free(zone, pfn - zone->base);
1062
1063 spinlock_unlock(&zone->lock);
1064
1065 /*
1066 * Signal that some memory has been freed.
1067 */
1068 mutex_lock(&zones_mtx);
1069 new_freed_mem = true;
1070 condvar_broadcast(&zones_cv);
1071 mutex_unlock(&zones_mtx);
1072
1073 interrupts_restore(ipl);
1074}
1075
1076/** Add reference to frame.
1077 *
1078 * Find respective frame structure for supplied PFN and
1079 * increment frame reference count.
1080 *
1081 * @param pfn Frame number of the frame to be freed.
1082 */
1083void frame_reference_add(pfn_t pfn)
1084{
1085 ipl_t ipl;
1086 zone_t *zone;
1087 frame_t *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 frame = &zone->frames[pfn - zone->base];
1098 frame->refcount++;
1099
1100 spinlock_unlock(&zone->lock);
1101 interrupts_restore(ipl);
1102}
1103
1104/** Mark given range unavailable in frame zones. */
1105void frame_mark_unavailable(pfn_t start, count_t count)
1106{
1107 unsigned int i;
1108 zone_t *zone;
1109 unsigned int prefzone = 0;
1110
1111 for (i = 0; i < count; i++) {
1112 zone = find_zone_and_lock(start + i, &prefzone);
1113 if (!zone) /* PFN not found */
1114 continue;
1115 zone_mark_unavailable(zone, start + i - zone->base);
1116
1117 spinlock_unlock(&zone->lock);
1118 }
1119}
1120
1121/** Initialize physical memory management. */
1122void frame_init(void)
1123{
1124 if (config.cpu_active == 1) {
1125 zones.count = 0;
1126 spinlock_initialize(&zones.lock, "zones.lock");
1127 }
1128 /* Tell the architecture to create some memory */
1129 frame_arch_init();
1130 if (config.cpu_active == 1) {
1131 frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
1132 SIZE2FRAMES(config.kernel_size));
1133 frame_mark_unavailable(ADDR2PFN(KA2PA(config.stack_base)),
1134 SIZE2FRAMES(config.stack_size));
1135
1136 count_t i;
1137 for (i = 0; i < init.cnt; i++) {
1138 pfn_t pfn = ADDR2PFN(KA2PA(init.tasks[i].addr));
1139 frame_mark_unavailable(pfn,
1140 SIZE2FRAMES(init.tasks[i].size));
1141 }
1142
1143 if (ballocs.size)
1144 frame_mark_unavailable(ADDR2PFN(KA2PA(ballocs.base)),
1145 SIZE2FRAMES(ballocs.size));
1146
1147 /* Black list first frame, as allocating NULL would
1148 * fail in some places */
1149 frame_mark_unavailable(0, 1);
1150 }
1151
1152 mutex_initialize(&zones_mtx, MUTEX_ACTIVE); /* mimic spinlock */
1153 condvar_initialize(&zones_cv);
1154}
1155
1156
1157/** Return total size of all zones. */
1158uint64_t zone_total_size(void)
1159{
1160 zone_t *zone = NULL;
1161 unsigned int i;
1162 ipl_t ipl;
1163 uint64_t total = 0;
1164
1165 ipl = interrupts_disable();
1166 spinlock_lock(&zones.lock);
1167
1168 for (i = 0; i < zones.count; i++) {
1169 zone = zones.info[i];
1170 spinlock_lock(&zone->lock);
1171 total += (uint64_t) FRAMES2SIZE(zone->count);
1172 spinlock_unlock(&zone->lock);
1173 }
1174
1175 spinlock_unlock(&zones.lock);
1176 interrupts_restore(ipl);
1177
1178 return total;
1179}
1180
1181/** Prints list of zones. */
1182void zone_print_list(void)
1183{
1184 zone_t *zone = NULL;
1185 unsigned int i;
1186 ipl_t ipl;
1187
1188 ipl = interrupts_disable();
1189 spinlock_lock(&zones.lock);
1190
1191#ifdef __32_BITS__
1192 printf("# base address free frames busy frames\n");
1193 printf("-- ------------ ------------ ------------\n");
1194#endif
1195
1196#ifdef __64_BITS__
1197 printf("# base address free frames busy frames\n");
1198 printf("-- -------------------- ------------ ------------\n");
1199#endif
1200
1201 for (i = 0; i < zones.count; i++) {
1202 zone = zones.info[i];
1203 spinlock_lock(&zone->lock);
1204
1205#ifdef __32_BITS__
1206 printf("%-2u %10p %12" PRIc " %12" PRIc "\n",
1207 i, PFN2ADDR(zone->base), zone->free_count,
1208 zone->busy_count);
1209#endif
1210
1211#ifdef __64_BITS__
1212 printf("%-2u %18p %12" PRIc " %12" PRIc "\n", i,
1213 PFN2ADDR(zone->base), zone->free_count, zone->busy_count);
1214#endif
1215
1216 spinlock_unlock(&zone->lock);
1217 }
1218
1219 spinlock_unlock(&zones.lock);
1220 interrupts_restore(ipl);
1221}
1222
1223/** Prints zone details.
1224 *
1225 * @param num Zone base address or zone number.
1226 */
1227void zone_print_one(unsigned int num)
1228{
1229 zone_t *zone = NULL;
1230 ipl_t ipl;
1231 unsigned int i;
1232
1233 ipl = interrupts_disable();
1234 spinlock_lock(&zones.lock);
1235
1236 for (i = 0; i < zones.count; i++) {
1237 if ((i == num) || (PFN2ADDR(zones.info[i]->base) == num)) {
1238 zone = zones.info[i];
1239 break;
1240 }
1241 }
1242 if (!zone) {
1243 printf("Zone not found.\n");
1244 goto out;
1245 }
1246
1247 spinlock_lock(&zone->lock);
1248 printf("Memory zone information\n");
1249 printf("Zone base address: %p\n", PFN2ADDR(zone->base));
1250 printf("Zone size: %" PRIc " frames (%" PRIs " KB)\n", zone->count,
1251 SIZE2KB(FRAMES2SIZE(zone->count)));
1252 printf("Allocated space: %" PRIc " frames (%" PRIs " KB)\n",
1253 zone->busy_count, SIZE2KB(FRAMES2SIZE(zone->busy_count)));
1254 printf("Available space: %" PRIc " frames (%" PRIs " KB)\n",
1255 zone->free_count, SIZE2KB(FRAMES2SIZE(zone->free_count)));
1256 buddy_system_structure_print(zone->buddy_system, FRAME_SIZE);
1257 spinlock_unlock(&zone->lock);
1258
1259out:
1260 spinlock_unlock(&zones.lock);
1261 interrupts_restore(ipl);
1262}
1263
1264/** @}
1265 */
1266
Note: See TracBrowser for help on using the repository browser.