diff options
author | Patrick Williams <iawillia@us.ibm.com> | 2010-05-20 19:16:30 -0500 |
---|---|---|
committer | Patrick Williams <iawillia@us.ibm.com> | 2010-05-20 19:16:30 -0500 |
commit | d9d7e6c7247aaf5d2721d08a365e9c51ec18c870 (patch) | |
tree | 16f882f7d77eb293ac0417b43bb5680ee149efbd /src/kernel/pagemgr.C | |
parent | d5a43e2fa43f8178aaa7ca67a9615d3ab4e68dbb (diff) | |
download | talos-hostboot-d9d7e6c7247aaf5d2721d08a365e9c51ec18c870.tar.gz talos-hostboot-d9d7e6c7247aaf5d2721d08a365e9c51ec18c870.zip |
Create page manager.
Diffstat (limited to 'src/kernel/pagemgr.C')
-rw-r--r-- | src/kernel/pagemgr.C | 124 |
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); +} |