summaryrefslogtreecommitdiffstats
path: root/src/kernel/pagemgr.C
diff options
context:
space:
mode:
authorPatrick Williams <iawillia@us.ibm.com>2010-05-20 19:16:30 -0500
committerPatrick Williams <iawillia@us.ibm.com>2010-05-20 19:16:30 -0500
commitd9d7e6c7247aaf5d2721d08a365e9c51ec18c870 (patch)
tree16f882f7d77eb293ac0417b43bb5680ee149efbd /src/kernel/pagemgr.C
parentd5a43e2fa43f8178aaa7ca67a9615d3ab4e68dbb (diff)
downloadtalos-hostboot-d9d7e6c7247aaf5d2721d08a365e9c51ec18c870.tar.gz
talos-hostboot-d9d7e6c7247aaf5d2721d08a365e9c51ec18c870.zip
Create page manager.
Diffstat (limited to 'src/kernel/pagemgr.C')
-rw-r--r--src/kernel/pagemgr.C124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/kernel/pagemgr.C b/src/kernel/pagemgr.C
new file mode 100644
index 000000000..e5d9ec321
--- /dev/null
+++ b/src/kernel/pagemgr.C
@@ -0,0 +1,124 @@
+#include <kernel/pagemgr.H>
+#include <util/singleton.H>
+#include <kernel/console.H>
+
+void* PageManager::allocatePage(size_t n)
+{
+ PageManager& pmgr = Singleton<PageManager>::instance();
+ return pmgr._allocatePage(n);
+}
+
+void PageManager::freePage(void* p, size_t n)
+{
+ PageManager& pmgr = Singleton<PageManager>::instance();
+ return pmgr._freePage(p, n);
+}
+
+PageManager::PageManager()
+{
+ // NOTE: This will need to change once we support loading modules.
+
+ // Determine first page of un-allocated memory.
+ extern void* end_load_address;
+ uint64_t addr = (uint64_t)&end_load_address;
+ if (0 != (addr % PAGESIZE))
+ addr = (addr - (addr % PAGESIZE)) + PAGESIZE;
+
+ // Determine number of pages available.
+ page_t* page = (page_t*)((void*) addr);
+ size_t length = (MEMLEN - addr) / PAGESIZE;
+
+ // Display.
+ printk("Initializing PageManager with %zd pages starting at %llx...",
+ length,
+ (uint64_t)page);
+
+ // Clear page buckets.
+ for(int i = 0; i < BUCKETS; i++)
+ first_page[i] = NULL;
+
+ // Allocate pages to buckets.
+ size_t page_length = BUCKETS-1;
+ while(length > 0)
+ {
+ while (length < (1 << page_length))
+ page_length--;
+
+ page->next_page = first_page[page_length];
+ first_page[page_length] = page;
+ page = (page_t*)((uint64_t)page + (1 << page_length)*PAGESIZE);
+ length -= (1 << page_length);
+ }
+
+ printk("done\n");
+}
+
+void* PageManager::_allocatePage(size_t n)
+{
+ int which_bucket = 0;
+ while (n > (1 << which_bucket)) which_bucket++;
+
+ int retries = 0;
+ page_t* page = (page_t*)NULL;
+ while ((page == NULL) && (retries < 3))
+ {
+ page = pop_bucket(which_bucket);
+ retries++;
+ }
+
+ if (NULL == page)
+ {
+ // TODO: Add abort instead.
+ printk("Insufficient memory for allocation of size %zd!\n", n);
+ while(1);
+ }
+
+ return page;
+}
+
+void PageManager::_freePage(void* p, size_t n)
+{
+ if ((NULL == p) || (0 == n)) return;
+
+ int which_bucket = 0;
+ while (n > (1 << which_bucket)) which_bucket++;
+
+ push_bucket((page_t*)p, which_bucket);
+ return;
+}
+
+PageManager::page_t* PageManager::pop_bucket(size_t n)
+{
+ if (n >= BUCKETS) return NULL;
+
+ page_t* p = first_page[n];
+
+ if (NULL == p)
+ {
+ // Couldn't allocate from the correct size bucket, so split up an
+ // item from the next sized bucket.
+ p = pop_bucket(n+1);
+ push_bucket((page_t*) (((uint64_t)p) + (PAGESIZE * (1 << n))),
+ n);
+ }
+ else
+ {
+ // This bucket appears to have items in it, allocate one.
+ if (!__sync_bool_compare_and_swap(&first_page[n],
+ p,
+ p->next_page))
+ return pop_bucket(n);
+ }
+ return p;
+}
+
+void PageManager::push_bucket(page_t* p, size_t n)
+{
+ if (n >= BUCKETS) return;
+
+ p->next_page = first_page[n];
+ if (!__sync_bool_compare_and_swap(&first_page[n],
+ p->next_page,
+ p))
+ push_bucket(p, n);
+}
OpenPOWER on IntegriCloud