Prex Home / Browse Source - Prex Version: 0.9.0

root/sys/mem/page.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. page_alloc
  2. page_free
  3. page_reserve
  4. page_info
  5. page_init

   1 /*-
   2  * Copyright (c) 2005-2009, Kohsuke Ohtani
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the author nor the names of any co-contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * page.c - physical page allocator
  32  */
  33 
  34 /*
  35  * Simple list-based page allocator:
  36  *
  37  * When the remaining page is exhausted, what should we do ?
  38  * If the system can stop with panic() here, the error check of
  39  * many portions in kernel is not necessary, and kernel code can
  40  * become more simple. But, in general, even if a page is
  41  * exhausted, a kernel can not be stopped but it should return an
  42  * error and continue processing.  If the memory becomes short
  43  * during boot time, kernel and drivers can use panic() in that
  44  * case.
  45  */
  46 
  47 #include <kernel.h>
  48 #include <page.h>
  49 #include <sched.h>
  50 #include <hal.h>
  51 
  52 /*
  53  * The page structure is put on the head of the first page of
  54  * each free block.
  55  */
  56 struct page {
  57         struct page     *next;
  58         struct page     *prev;
  59         vsize_t         size;           /* number of bytes of this block */
  60 };
  61 
  62 static struct page      page_head;      /* first free block */
  63 static psize_t          total_size;     /* size of memory in the system */
  64 static psize_t          used_size;      /* current used size */
  65 static psize_t          bootdisk_size;  /* size of the boot disk */
  66 
  67 /*
  68  * page_alloc - allocate continuous pages of the specified size.
  69  *
  70  * This routine returns the physical address of a new free page
  71  * block, or returns NULL on failure. The requested size is
  72  * automatically round up to the page boundary.  The allocated
  73  * memory is _not_ filled with 0.
  74  */
  75 paddr_t
  76 page_alloc(psize_t psize)
  77 {
  78         struct page *blk, *tmp;
  79         vsize_t size;
  80 
  81         ASSERT(psize != 0);
  82 
  83         sched_lock();
  84 
  85         /*
  86          * Find the free block that has enough size.
  87          */
  88         size = round_page(psize);
  89         blk = &page_head;
  90         do {
  91                 blk = blk->next;
  92                 if (blk == &page_head) {
  93                         sched_unlock();
  94                         DPRINTF(("page_alloc: out of memory\n"));
  95                         return 0;       /* Not found. */
  96                 }
  97         } while (blk->size < size);
  98 
  99         /*
 100          * If found block size is exactly same with requested,
 101          * just remove it from a free list. Otherwise, the
 102          * found block is divided into two and first half is
 103          * used for allocation.
 104          */
 105         if (blk->size == size) {
 106                 blk->prev->next = blk->next;
 107                 blk->next->prev = blk->prev;
 108         } else {
 109                 tmp = (struct page *)((vaddr_t)blk + size);
 110                 tmp->size = blk->size - size;
 111                 tmp->prev = blk->prev;
 112                 tmp->next = blk->next;
 113                 blk->prev->next = tmp;
 114                 blk->next->prev = tmp;
 115         }
 116         used_size += (psize_t)size;
 117         sched_unlock();
 118         return kvtop(blk);
 119 }
 120 
 121 /*
 122  * Free page block.
 123  *
 124  * This allocator does not maintain the size of allocated page
 125  * block. The caller must provide the size information of the
 126  * block.
 127  */
 128 void
 129 page_free(paddr_t paddr, psize_t psize)
 130 {
 131         struct page *blk, *prev;
 132         vsize_t size;
 133 
 134         ASSERT(psize != 0);
 135 
 136         sched_lock();
 137 
 138         size = round_page(psize);
 139         blk = ptokv(paddr);
 140 
 141         /*
 142          * Find the target position in list.
 143          */
 144         for (prev = &page_head; prev->next < blk; prev = prev->next) {
 145                 if (prev->next == &page_head)
 146                         break;
 147         }
 148 
 149         /*
 150          * Insert new block into list.
 151          */
 152         blk->size = size;
 153         blk->prev = prev;
 154         blk->next = prev->next;
 155         prev->next->prev = blk;
 156         prev->next = blk;
 157 
 158         /*
 159          * If the adjoining block is free, it combines and
 160          * is made on block.
 161          */
 162         if (blk->next != &page_head &&
 163             ((vaddr_t)blk + blk->size) == (vaddr_t)blk->next) {
 164                 blk->size += blk->next->size;
 165                 blk->next = blk->next->next;
 166                 blk->next->prev = blk;
 167         }
 168         if (blk->prev != &page_head &&
 169             (vaddr_t)blk->prev + blk->prev->size == (vaddr_t)blk) {
 170                 blk->prev->size += blk->size;
 171                 blk->prev->next = blk->next;
 172                 blk->next->prev = blk->prev;
 173         }
 174         used_size -= (psize_t)size;
 175         sched_unlock();
 176 }
 177 
 178 /*
 179  * The function to reserve pages in specific address.
 180  */
 181 int
 182 page_reserve(paddr_t paddr, psize_t psize)
 183 {
 184         struct page *blk, *tmp;
 185         vaddr_t start, end;
 186         vsize_t size;
 187 
 188         if (psize == 0)
 189                 return 0;
 190 
 191         start = trunc_page((vaddr_t)ptokv(paddr));
 192         end = round_page((vaddr_t)ptokv(paddr + psize));
 193         size = end - start;
 194 
 195         /*
 196          * Find the block which includes specified block.
 197          */
 198         blk = page_head.next;
 199         for (;;) {
 200                 if (blk == &page_head)
 201                         return ENOMEM;
 202 
 203                 if ((vaddr_t)blk <= start
 204                     && end <= (vaddr_t)blk + blk->size)
 205                         break;
 206                 blk = blk->next;
 207         }
 208         if ((vaddr_t)blk == start && blk->size == size) {
 209                 /*
 210                  * Unlink the block from free list.
 211                  */
 212                 blk->prev->next = blk->next;
 213                 blk->next->prev = blk->prev;
 214         } else {
 215                 /*
 216                  * Split this block.
 217                  */
 218                 if ((vaddr_t)blk + blk->size != end) {
 219                         tmp = (struct page *)end;
 220                         tmp->size = (vaddr_t)blk + blk->size - end;
 221                         tmp->next = blk->next;
 222                         tmp->prev = blk;
 223 
 224                         blk->size -= tmp->size;
 225                         blk->next->prev = tmp;
 226                         blk->next = tmp;
 227                 }
 228                 if ((vaddr_t)blk == start) {
 229                         blk->prev->next = blk->next;
 230                         blk->next->prev = blk->prev;
 231                 } else
 232                         blk->size = start - (vaddr_t)blk;
 233         }
 234         used_size += (psize_t)size;
 235         return 0;
 236 }
 237 
 238 void
 239 page_info(struct meminfo *info)
 240 {
 241 
 242         info->total = total_size;
 243         info->free = total_size - used_size;
 244         info->bootdisk = bootdisk_size;
 245 
 246 #ifndef CONFIG_ROMBOOT
 247         /*
 248          * The boot disk is placed at RAM.
 249          */
 250         info->free -= bootdisk_size;
 251 #endif
 252 }
 253 
 254 /*
 255  * Initialize page allocator.
 256  * page_init() must be called prior to other memory manager's
 257  * initializations.
 258  */
 259 void
 260 page_init(void)
 261 {
 262         struct physmem *ram;
 263         struct bootinfo *bi;
 264         int i;
 265 
 266         machine_bootinfo(&bi);
 267 
 268         total_size = 0;
 269         bootdisk_size = 0;
 270         page_head.next = page_head.prev = &page_head;
 271 
 272         /*
 273          * First, create a free list from the boot information.
 274          */
 275         for (i = 0; i < bi->nr_rams; i++) {
 276                 ram = &bi->ram[i];
 277                 if (ram->type == MT_USABLE) {
 278                         page_free(ram->base, ram->size);
 279                         total_size += ram->size;
 280                 }
 281         }
 282         /*
 283          * Then, reserve un-usable memory.
 284          */
 285         for (i = 0; i < bi->nr_rams; i++) {
 286                 ram = &bi->ram[i];
 287                 switch (ram->type) {
 288                 case MT_BOOTDISK:
 289                         bootdisk_size += ram->size;
 290                         /* FALLTHROUGH */
 291                 case MT_MEMHOLE:
 292                         total_size -= ram->size;
 293                         /* FALLTHROUGH */
 294                 case MT_RESERVED:
 295                         if (page_reserve(ram->base, ram->size))
 296                                 panic("page_init");
 297                         break;
 298                 }
 299         }
 300         used_size = 0;
 301         DPRINTF(("Memory size=%ld\n", total_size));
 302 }

/* [<][>][^][v][top][bottom][index][help] */