mirror of
https://github.com/drasko/codezero.git
synced 2026-02-11 09:23:15 +01:00
Greatly simplified page allocator.
Made most changes for alloc_page()
This commit is contained in:
@@ -18,77 +18,44 @@
|
|||||||
|
|
||||||
struct page_allocator allocator;
|
struct page_allocator allocator;
|
||||||
|
|
||||||
static struct mem_cache *new_dcache();
|
|
||||||
static int find_and_free_page_area(void *addr, struct page_allocator *p);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a new page area from @area_sources_start. If no areas left,
|
* Allocate a new page area from the page area cache
|
||||||
* allocate a new cache first, allocate page area from the new cache.
|
|
||||||
*/
|
*/
|
||||||
static struct page_area *new_page_area(struct page_allocator *p,
|
static struct page_area *new_page_area(struct page_allocator *p)
|
||||||
struct list_head *ccache)
|
|
||||||
{
|
{
|
||||||
struct mem_cache *cache;
|
struct mem_cache *cache;
|
||||||
struct page_area *new_area;
|
struct page_area *new_area;
|
||||||
struct list_head *cache_list;
|
|
||||||
|
|
||||||
if (ccache)
|
list_for_each_entry(cache, &p->pga_cache_list, list) {
|
||||||
cache_list = ccache;
|
|
||||||
else
|
|
||||||
cache_list = &p->dcache_list;
|
|
||||||
|
|
||||||
list_for_each_entry(cache, cache_list, list)
|
|
||||||
if ((new_area = mem_cache_alloc(cache)) != 0) {
|
if ((new_area = mem_cache_alloc(cache)) != 0) {
|
||||||
new_area->cache = cache;
|
new_area->cache = cache;
|
||||||
|
p->pga_free--;
|
||||||
return new_area;
|
return new_area;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* Must not reach here if a ccache is already used. */
|
return 0;
|
||||||
BUG_ON(ccache);
|
|
||||||
|
|
||||||
if ((cache = new_dcache(p)) == 0)
|
|
||||||
return 0; /* Denotes out of memory */
|
|
||||||
|
|
||||||
new_area = mem_cache_alloc(cache);
|
|
||||||
new_area->cache = cache;
|
|
||||||
return new_area;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Given the page @quantity, finds a free region, divides and returns new area. */
|
/* Given the page @quantity, finds a free region, divides and returns new area. */
|
||||||
static struct page_area *
|
static struct page_area *
|
||||||
get_free_page_area(int quantity, struct page_allocator *p,
|
get_free_page_area(int quantity, struct page_allocator *p)
|
||||||
struct list_head *cache_list)
|
|
||||||
{
|
{
|
||||||
struct page_area *new, *area;
|
struct page_area *new, *area;
|
||||||
|
|
||||||
if (quantity <= 0)
|
if (quantity <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* First, allocate a new area, which may involve recursion.
|
|
||||||
* If we call this while we touch the global area list, we will
|
|
||||||
* corrupt it so we call it first.
|
|
||||||
*/
|
|
||||||
if (!(new = new_page_area(p, cache_list)))
|
|
||||||
return 0; /* No more pages */
|
|
||||||
|
|
||||||
list_for_each_entry(area, &p->page_area_list, list) {
|
list_for_each_entry(area, &p->page_area_list, list) {
|
||||||
|
|
||||||
/* Check for exact size match */
|
/* Check for exact size match */
|
||||||
if (area->numpages == quantity && !area->used) {
|
if (area->numpages == quantity && !area->used) {
|
||||||
/* Mark it as used */
|
|
||||||
area->used = 1;
|
area->used = 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't need the area we allocated
|
|
||||||
* earlier, just free it.
|
|
||||||
*/
|
|
||||||
BUG_ON(find_and_free_page_area(
|
|
||||||
(void *)__pfn_to_addr(new->pfn), p) < 0);
|
|
||||||
return area;
|
return area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Divide a bigger area */
|
||||||
if (area->numpages > quantity && !area->used) {
|
if (area->numpages > quantity && !area->used) {
|
||||||
|
new = new_page_area(p);
|
||||||
area->numpages -= quantity;
|
area->numpages -= quantity;
|
||||||
new->pfn = area->pfn + area->numpages;
|
new->pfn = area->pfn + area->numpages;
|
||||||
new->numpages = quantity;
|
new->numpages = quantity;
|
||||||
@@ -99,92 +66,10 @@ get_free_page_area(int quantity, struct page_allocator *p,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* No more pages */
|
||||||
* No more pages. We could not use the area
|
|
||||||
* we allocated earlier, just free it.
|
|
||||||
*/
|
|
||||||
find_and_free_page_area((void *)__pfn_to_addr(new->pfn), p);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *alloc_page(int quantity)
|
|
||||||
{
|
|
||||||
struct page_area *p = get_free_page_area(quantity, &allocator, 0);
|
|
||||||
|
|
||||||
if (p)
|
|
||||||
return (void *)__pfn_to_addr(p->pfn);
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper to allocate a page using an internal page area cache. Returns
|
|
||||||
* a virtual address because these allocations are always internally referenced.
|
|
||||||
*/
|
|
||||||
void *alloc_page_using_cache(struct page_allocator *a, struct list_head *c)
|
|
||||||
{
|
|
||||||
struct page_area *p = get_free_page_area(1, a, c);
|
|
||||||
|
|
||||||
if (p)
|
|
||||||
return l4_map_helper((void *)__pfn_to_addr(p->pfn), 1);
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There's still free memory, but allocator ran out of page areas stored in
|
|
||||||
* dcaches. In this case, the ccache supplies a new page area, which is used to
|
|
||||||
* describe a page that stores a new dcache. If ccache is also out of page areas
|
|
||||||
* it adds the spare cache to ccache_list, uses that for the current allocation,
|
|
||||||
* and allocates a new spare cache for future use
|
|
||||||
*/
|
|
||||||
static struct mem_cache *new_dcache(struct page_allocator *p)
|
|
||||||
{
|
|
||||||
void *dpage; /* Page that keeps data cache */
|
|
||||||
void *spage; /* Page that keeps spare cache */
|
|
||||||
|
|
||||||
if((dpage = alloc_page_using_cache(p, &p->ccache_list)))
|
|
||||||
return mem_cache_init(dpage, PAGE_SIZE,
|
|
||||||
sizeof(struct page_area), 0);
|
|
||||||
|
|
||||||
/* If ccache is also full, add the spare page_area cache to ccache */
|
|
||||||
list_add(&p->spare->list, &p->ccache_list);
|
|
||||||
|
|
||||||
/* This must not fail now, and satisfy at least two page requests. */
|
|
||||||
BUG_ON(mem_cache_total_empty(p->spare) < 2);
|
|
||||||
BUG_ON(!(dpage = alloc_page_using_cache(p, &p->ccache_list)));
|
|
||||||
BUG_ON(!(spage = alloc_page_using_cache(p, &p->ccache_list)));
|
|
||||||
|
|
||||||
/* Initialise the new spare and return the new dcache. */
|
|
||||||
p->spare = mem_cache_init(spage, PAGE_SIZE, sizeof(struct page_area),0);
|
|
||||||
return mem_cache_init(dpage, PAGE_SIZE, sizeof(struct page_area), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only to be used at initialisation. Allocates memory caches that contain
|
|
||||||
* page_area elements by incrementing the free physical memory mark by
|
|
||||||
* PAGE_SIZE.
|
|
||||||
*/
|
|
||||||
static struct mem_cache *new_allocator_startup_cache(unsigned long *start)
|
|
||||||
{
|
|
||||||
struct page_area *area;
|
|
||||||
struct mem_cache *cache;
|
|
||||||
|
|
||||||
cache = mem_cache_init(l4_map_helper((void *)*start, 1), PAGE_SIZE,
|
|
||||||
sizeof(struct page_area), 0);
|
|
||||||
area = mem_cache_alloc(cache);
|
|
||||||
|
|
||||||
/* Initialising the dummy just for illustration */
|
|
||||||
area->pfn = __pfn(*start);
|
|
||||||
area->numpages = 1;
|
|
||||||
area->cache = cache;
|
|
||||||
INIT_LIST_HEAD(&area->list);
|
|
||||||
|
|
||||||
/* FIXME: Should I add this to the page area list? */
|
|
||||||
*start += PAGE_SIZE;
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All physical memory is tracked by a simple linked list implementation. A
|
* All physical memory is tracked by a simple linked list implementation. A
|
||||||
@@ -199,40 +84,113 @@ static struct mem_cache *new_allocator_startup_cache(unsigned long *start)
|
|||||||
* Also other memory regions like IO are not tracked by alloc_page() but by
|
* Also other memory regions like IO are not tracked by alloc_page() but by
|
||||||
* other means.
|
* other means.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void init_page_allocator(unsigned long start, unsigned long end)
|
void init_page_allocator(unsigned long start, unsigned long end)
|
||||||
{
|
{
|
||||||
struct page_area *freemem;
|
/* Initialise a page area cache in the first page */
|
||||||
struct mem_cache *dcache, *ccache;
|
struct page_area *freemem, *area;
|
||||||
|
struct mem_cache *cache;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&allocator.dcache_list);
|
|
||||||
INIT_LIST_HEAD(&allocator.ccache_list);
|
|
||||||
INIT_LIST_HEAD(&allocator.page_area_list);
|
INIT_LIST_HEAD(&allocator.page_area_list);
|
||||||
|
INIT_LIST_HEAD(&allocator.pga_cache_list);
|
||||||
|
|
||||||
/* Primary cache list that stores page areas of regular data. */
|
/* Initialise the first page area cache */
|
||||||
dcache = new_allocator_startup_cache(&start);
|
cache = mem_cache_init(l4_map_helper((void *)start, 1), PAGE_SIZE,
|
||||||
list_add(&dcache->list, &allocator.dcache_list);
|
sizeof(struct page_area), 0);
|
||||||
|
list_add(&cache->list, &allocator.pga_cache_list);
|
||||||
|
|
||||||
/* The secondary cache list that stores page areas of caches */
|
/* Initialise the first area that describes the page just allocated */
|
||||||
ccache = new_allocator_startup_cache(&start);
|
area = mem_cache_alloc(cache);
|
||||||
list_add(&ccache->list, &allocator.ccache_list);
|
INIT_LIST_HEAD(&area->list);
|
||||||
|
area->pfn = __pfn(start);
|
||||||
|
area->used = 1;
|
||||||
|
area->numpages = 1;
|
||||||
|
area->cache = cache;
|
||||||
|
list_add(&area->list, &allocator.page_area_list);
|
||||||
|
|
||||||
|
/* Update freemem start address */
|
||||||
|
start += PAGE_SIZE;
|
||||||
|
|
||||||
/* Initialise first area that describes all of free physical memory */
|
/* Initialise first area that describes all of free physical memory */
|
||||||
freemem = mem_cache_alloc(dcache);
|
freemem = mem_cache_alloc(cache);
|
||||||
INIT_LIST_HEAD(&freemem->list);
|
INIT_LIST_HEAD(&freemem->list);
|
||||||
freemem->pfn = __pfn(start);
|
freemem->pfn = __pfn(start);
|
||||||
freemem->numpages = __pfn(end) - freemem->pfn;
|
freemem->numpages = __pfn(end) - freemem->pfn;
|
||||||
freemem->cache = dcache;
|
freemem->cache = cache;
|
||||||
freemem->used = 0;
|
freemem->used = 0;
|
||||||
|
|
||||||
/* Add it as the first unused page area */
|
/* Add it as the first unused page area */
|
||||||
list_add(&freemem->list, &allocator.page_area_list);
|
list_add(&freemem->list, &allocator.page_area_list);
|
||||||
|
|
||||||
/* Allocate and add the spare page area cache */
|
/* Initialise free page area counter */
|
||||||
allocator.spare = mem_cache_init(l4_map_helper(alloc_page(1), 1),
|
allocator.pga_free = mem_cache_total_empty(cache);
|
||||||
PAGE_SIZE, sizeof(struct page_area),
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we're about to run out of free page area structures.
|
||||||
|
* If so, allocate a new cache of page areas.
|
||||||
|
*/
|
||||||
|
int check_page_areas(struct page_allocator *p)
|
||||||
|
{
|
||||||
|
struct page_area *new;
|
||||||
|
struct mem_cache *newcache;
|
||||||
|
void *newpage;
|
||||||
|
|
||||||
|
/* If only one free area left */
|
||||||
|
if (p->pga_free == 1) {
|
||||||
|
|
||||||
|
/* Use that area to allocate a new page */
|
||||||
|
if (!(new = get_free_page_area(1, p)))
|
||||||
|
return -1; /* Out of memory */
|
||||||
|
|
||||||
|
/* Free page areas must now be reduced to 0 */
|
||||||
|
BUG_ON(p->pga_free != 0);
|
||||||
|
|
||||||
|
/* Map the new page into virtual memory */
|
||||||
|
newpage = l4_map_helper((void *)__pfn_to_addr(new->pfn), 1);
|
||||||
|
|
||||||
|
/* Initialise it as a new source of page area structures */
|
||||||
|
newcache = mem_cache_init(newpage, PAGE_SIZE,
|
||||||
|
sizeof(struct page_area), 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the free page area counter
|
||||||
|
* NOTE: need to lock the allocator here
|
||||||
|
*/
|
||||||
|
p->pga_free += mem_cache_total_empty(newcache);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the new cache to available
|
||||||
|
* list of free page area caches
|
||||||
|
*/
|
||||||
|
list_add(&newcache->list, &p->pga_cache_list);
|
||||||
|
/* Unlock here */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *alloc_page(int quantity)
|
||||||
|
{
|
||||||
|
struct page_area *new;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First make sure we have enough page
|
||||||
|
* area structures in the cache
|
||||||
|
*/
|
||||||
|
if (check_page_areas(&allocator) < 0)
|
||||||
|
return 0; /* Out of memory */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now allocate the actual pages, using the available
|
||||||
|
* page area structures to describe the allocation
|
||||||
|
*/
|
||||||
|
new = get_free_page_area(quantity, &allocator);
|
||||||
|
|
||||||
|
/* Return physical address */
|
||||||
|
return (void *)__pfn_to_addr(new->pfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Merges two page areas, frees area cache if empty, returns the merged area. */
|
/* Merges two page areas, frees area cache if empty, returns the merged area. */
|
||||||
struct page_area *merge_free_areas(struct page_area *before,
|
struct page_area *merge_free_areas(struct page_area *before,
|
||||||
struct page_area *after)
|
struct page_area *after)
|
||||||
@@ -249,8 +207,10 @@ struct page_area *merge_free_areas(struct page_area *before,
|
|||||||
mem_cache_free(c, after);
|
mem_cache_free(c, after);
|
||||||
|
|
||||||
/* Recursively free the cache page */
|
/* Recursively free the cache page */
|
||||||
if (mem_cache_is_empty(c))
|
if (mem_cache_is_empty(c)) {
|
||||||
|
list_del(&c->list);
|
||||||
BUG_ON(free_page(l4_unmap_helper(c, 1)) < 0)
|
BUG_ON(free_page(l4_unmap_helper(c, 1)) < 0)
|
||||||
|
}
|
||||||
return before;
|
return before;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +219,7 @@ static int find_and_free_page_area(void *addr, struct page_allocator *p)
|
|||||||
struct page_area *area, *prev, *next;
|
struct page_area *area, *prev, *next;
|
||||||
|
|
||||||
/* First find the page area to be freed. */
|
/* First find the page area to be freed. */
|
||||||
list_for_each_entry(area, &p->dcache_list, list)
|
list_for_each_entry(area, &p->page_area_list, list)
|
||||||
if (__pfn_to_addr(area->pfn) == (unsigned long)addr &&
|
if (__pfn_to_addr(area->pfn) == (unsigned long)addr &&
|
||||||
area->used) { /* Found it */
|
area->used) { /* Found it */
|
||||||
area->used = 0;
|
area->used = 0;
|
||||||
@@ -269,12 +229,12 @@ static int find_and_free_page_area(void *addr, struct page_allocator *p)
|
|||||||
|
|
||||||
found:
|
found:
|
||||||
/* Now merge with adjacent areas, if possible */
|
/* Now merge with adjacent areas, if possible */
|
||||||
if (area->list.prev != &p->dcache_list) {
|
if (area->list.prev != &p->page_area_list) {
|
||||||
prev = list_entry(area->list.prev, struct page_area, list);
|
prev = list_entry(area->list.prev, struct page_area, list);
|
||||||
if (!prev->used)
|
if (!prev->used)
|
||||||
area = merge_free_areas(prev, area);
|
area = merge_free_areas(prev, area);
|
||||||
}
|
}
|
||||||
if (area->list.next != &p->dcache_list) {
|
if (area->list.next != &p->page_area_list) {
|
||||||
next = list_entry(area->list.next, struct page_area, list);
|
next = list_entry(area->list.next, struct page_area, list);
|
||||||
if (!next->used)
|
if (!next->used)
|
||||||
area = merge_free_areas(area, next);
|
area = merge_free_areas(area, next);
|
||||||
|
|||||||
@@ -15,14 +15,9 @@ struct page_area {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct page_allocator {
|
struct page_allocator {
|
||||||
/* Keep track of page area lists and allocated caches for page areas. */
|
|
||||||
struct list_head page_area_list;
|
struct list_head page_area_list;
|
||||||
/* Caches of page areas that refer to any kind of data */
|
struct list_head pga_cache_list;
|
||||||
struct list_head dcache_list;
|
int pga_free;
|
||||||
/* Caches of page areas that refer to cache pages */
|
|
||||||
struct list_head ccache_list;
|
|
||||||
/* A spare cache to aid when both caches are full */
|
|
||||||
struct mem_cache *spare;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Initialises the page allocator */
|
/* Initialises the page allocator */
|
||||||
|
|||||||
Reference in New Issue
Block a user