summaryrefslogtreecommitdiffstats
path: root/src/kernel/pagemgr.C
blob: 2a92df206d154ed4555a46945794c6781b6f7adc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <kernel/pagemgr.H>
#include <util/singleton.H>
#include <kernel/console.H>
#include <sys/vfs.h>

void PageManager::init()
{
    Singleton<PageManager>::instance();
}

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()
{
    // Determine first page of un-allocated memory.
    uint64_t addr = (uint64_t) VFS_LAST_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);
    
    // Allocate pages to buckets.
    size_t page_length = BUCKETS-1;
    while(length > 0)
    {
	while (length < (size_t)(1 << page_length))
	    page_length--;

	first_page[page_length].push(page);
	page = (page_t*)((uint64_t)page + (1 << page_length)*PAGESIZE);
	length -= (1 << page_length);
    }

    printk("done\n");
}

void* PageManager::_allocatePage(size_t n)
{
    size_t which_bucket = 0;
    while (n > (size_t)(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;

    size_t which_bucket = 0;
    while (n > (size_t)(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].pop();

    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);
	if (NULL != p)
	{
	    push_bucket((page_t*) (((uint64_t)p) + (PAGESIZE * (1 << n))),
			n);
	}
    }
    return p;
}

void PageManager::push_bucket(page_t* p, size_t n)
{
    if (n >= BUCKETS) return;
    first_page[n].push(p);
}
OpenPOWER on IntegriCloud