forked from KolibriOS/kolibrios
4ad5e0815b
git-svn-id: svn://kolibrios.org@1066 a494cfbc-eb01-0410-851d-a64ba20cac60
369 lines
8.1 KiB
C++
369 lines
8.1 KiB
C++
|
|
#define BUDDY_SYSTEM_INNER_BLOCK 0xff
|
|
|
|
#define frame_index( frame ) \
|
|
(index_t)( (frame) - z_core.frames)
|
|
|
|
#define frame_initialize( frame ) \
|
|
(frame)->refcount = 1; \
|
|
(frame)->buddy_order = 0
|
|
|
|
#define buddy_get_order( block) \
|
|
((frame_t*)(block))->buddy_order
|
|
|
|
#define buddy_set_order( block, order) \
|
|
((frame_t*)(block))->buddy_order = (order)
|
|
|
|
#define buddy_mark_busy( block ) \
|
|
((frame_t*)(block))->refcount = 1
|
|
|
|
#define IS_BUDDY_LEFT_BLOCK(frame) \
|
|
(((frame_index((frame)) >> (frame)->buddy_order) & 0x1) == 0)
|
|
|
|
#define IS_BUDDY_RIGHT_BLOCK(frame) \
|
|
(((frame_index((frame)) >> (frame)->buddy_order) & 0x1) == 1)
|
|
|
|
#define buddy_mark_available( block ) \
|
|
((frame_t*)(block))->refcount = 0
|
|
|
|
|
|
static __inline link_t * buddy_bisect(link_t *block)
|
|
{
|
|
frame_t *frame_l, *frame_r;
|
|
|
|
frame_l = (frame_t*)block;
|
|
frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
|
|
|
|
return &frame_r->buddy_link;
|
|
}
|
|
|
|
static __inline link_t *buddy_coalesce(link_t *block_1, link_t *block_2)
|
|
{
|
|
frame_t *frame1, *frame2;
|
|
|
|
frame1 = (frame_t*)block_1;
|
|
frame2 = (frame_t*)block_2;
|
|
|
|
return frame1 < frame2 ? block_1 : block_2;
|
|
}
|
|
|
|
static link_t *find_buddy(link_t *block)
|
|
{
|
|
frame_t *frame;
|
|
index_t index;
|
|
u32_t is_left, is_right;
|
|
|
|
frame = (frame_t*)block;
|
|
// ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),frame->buddy_order));
|
|
|
|
is_left = IS_BUDDY_LEFT_BLOCK( frame);
|
|
is_right = IS_BUDDY_RIGHT_BLOCK( frame);
|
|
|
|
// ASSERT(is_left ^ is_right);
|
|
if (is_left) {
|
|
index = (frame_index(frame)) + (1 << frame->buddy_order);
|
|
} else { /* if (is_right) */
|
|
index = (frame_index(frame)) - (1 << frame->buddy_order);
|
|
}
|
|
|
|
if ( index < z_core.count)
|
|
{
|
|
if (z_core.frames[index].buddy_order == frame->buddy_order &&
|
|
z_core.frames[index].refcount == 0) {
|
|
return &z_core.frames[index].buddy_link;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static link_t *buddy_find_block(link_t *child, u32_t order)
|
|
{
|
|
frame_t *frame;
|
|
index_t index;
|
|
|
|
frame = (frame_t*)child;
|
|
|
|
index = frame_index(frame);
|
|
do {
|
|
if (z_core.frames[index].buddy_order != order)
|
|
return &z_core.frames[index].buddy_link;
|
|
|
|
} while(index-- > 0);
|
|
return NULL;
|
|
}
|
|
|
|
static void buddy_system_free(link_t *block)
|
|
{
|
|
link_t *buddy, *hlp;
|
|
u32_t i;
|
|
|
|
/*
|
|
* Determine block's order.
|
|
*/
|
|
i = buddy_get_order(block);
|
|
|
|
// ASSERT(i <= z->max_order);
|
|
|
|
if (i != z_core.max_order)
|
|
{
|
|
/*
|
|
* See if there is any buddy in the list of order i.
|
|
*/
|
|
buddy = find_buddy( block );
|
|
if (buddy)
|
|
{
|
|
|
|
// ASSERT(buddy_get_order(z, buddy) == i);
|
|
/*
|
|
* Remove buddy from the list of order i.
|
|
*/
|
|
list_remove(buddy);
|
|
|
|
/*
|
|
* Invalidate order of both block and buddy.
|
|
*/
|
|
buddy_set_order(block, BUDDY_SYSTEM_INNER_BLOCK);
|
|
buddy_set_order(buddy, BUDDY_SYSTEM_INNER_BLOCK);
|
|
|
|
/*
|
|
* Coalesce block and buddy into one block.
|
|
*/
|
|
hlp = buddy_coalesce( block, buddy );
|
|
|
|
/*
|
|
* Set order of the coalesced block to i + 1.
|
|
*/
|
|
buddy_set_order(hlp, i + 1);
|
|
|
|
/*
|
|
* Recursively add the coalesced block to the list of order i + 1.
|
|
*/
|
|
buddy_system_free( hlp );
|
|
return;
|
|
}
|
|
}
|
|
/*
|
|
* Insert block into the list of order i.
|
|
*/
|
|
list_append(block, &z_core.order[i]);
|
|
}
|
|
|
|
|
|
static link_t* buddy_alloc( u32_t i)
|
|
{
|
|
link_t *res, *hlp;
|
|
|
|
ASSERT(i <= z_core.max_order);
|
|
|
|
/*
|
|
* If the list of order i is not empty,
|
|
* the request can be immediatelly satisfied.
|
|
*/
|
|
if (!list_empty(&z_core.order[i])) {
|
|
res = z_core.order[i].next;
|
|
list_remove(res);
|
|
buddy_mark_busy(res);
|
|
return res;
|
|
}
|
|
/*
|
|
* If order i is already the maximal order,
|
|
* the request cannot be satisfied.
|
|
*/
|
|
if (i == z_core.max_order)
|
|
return NULL;
|
|
|
|
/*
|
|
* Try to recursively satisfy the request from higher order lists.
|
|
*/
|
|
hlp = buddy_alloc( i + 1 );
|
|
|
|
/*
|
|
* The request could not be satisfied
|
|
* from higher order lists.
|
|
*/
|
|
if (!hlp)
|
|
return NULL;
|
|
|
|
res = hlp;
|
|
|
|
/*
|
|
* Bisect the block and set order of both of its parts to i.
|
|
*/
|
|
hlp = buddy_bisect( res );
|
|
|
|
buddy_set_order(res, i);
|
|
buddy_set_order(hlp, i);
|
|
|
|
/*
|
|
* Return the other half to buddy system. Mark the first part
|
|
* full, so that it won't coalesce again.
|
|
*/
|
|
buddy_mark_busy(res);
|
|
buddy_system_free( hlp );
|
|
|
|
return res;
|
|
}
|
|
|
|
static link_t* buddy_alloc_block(link_t *block)
|
|
{
|
|
link_t *left,*right, *tmp;
|
|
u32_t order;
|
|
|
|
left = buddy_find_block(block, BUDDY_SYSTEM_INNER_BLOCK);
|
|
ASSERT(left);
|
|
list_remove(left);
|
|
while (1)
|
|
{
|
|
if ( !buddy_get_order(left))
|
|
{
|
|
buddy_mark_busy(left);
|
|
return left;
|
|
}
|
|
|
|
order = buddy_get_order(left);
|
|
|
|
right = buddy_bisect(left);
|
|
buddy_set_order(left, order-1);
|
|
buddy_set_order(right, order-1);
|
|
|
|
tmp = buddy_find_block( block, BUDDY_SYSTEM_INNER_BLOCK);
|
|
|
|
if (tmp == right) {
|
|
right = left;
|
|
left = tmp;
|
|
}
|
|
ASSERT(tmp == left);
|
|
buddy_mark_busy(left);
|
|
buddy_system_free(right);
|
|
buddy_mark_available(left);
|
|
}
|
|
}
|
|
|
|
static void zone_create(zone_t *z, pfn_t start, count_t count)
|
|
{
|
|
unsigned int i;
|
|
|
|
spinlock_initialize(&z->lock);
|
|
|
|
z->base = start;
|
|
z->count = count;
|
|
z->free_count = count;
|
|
z->busy_count = 0;
|
|
|
|
z->max_order = fnzb(count);
|
|
|
|
ASSERT(z->max_order < BUDDY_SYSTEM_INNER_BLOCK);
|
|
|
|
for (i = 0; i <= z->max_order; i++)
|
|
list_initialize(&z->order[i]);
|
|
|
|
z->frames = (frame_t *)balloc(count*sizeof(frame_t));
|
|
|
|
for (i = 0; i < count; i++)
|
|
frame_initialize(&z->frames[i]);
|
|
|
|
/*
|
|
for (i = 0; i < count; i++) {
|
|
z_core.frames[i].buddy_order=0;
|
|
z_core.frames[i].parent = NULL;
|
|
z_core.frames[i].refcount=1;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
z_core.frames[i].refcount = 0;
|
|
buddy_system_free(&z_core.frames[i].buddy_link);
|
|
}
|
|
*/
|
|
|
|
DBG("create zone: base %x count %x order %d\n",
|
|
start, count, z->max_order);
|
|
|
|
}
|
|
|
|
static void zone_mark_unavailable(zone_t *zone, index_t frame_idx)
|
|
{
|
|
frame_t *frame;
|
|
link_t *link;
|
|
|
|
ASSERT(frame_idx < zone->count);
|
|
|
|
frame = &zone->frames[frame_idx];
|
|
|
|
if (frame->refcount)
|
|
return;
|
|
link = buddy_alloc_block( &frame->buddy_link);
|
|
ASSERT(link);
|
|
zone->free_count--;
|
|
}
|
|
|
|
static void zone_reserve(zone_t *z, pfn_t base, count_t count)
|
|
{
|
|
int i;
|
|
pfn_t top = base + count;
|
|
|
|
if( (base+count < z->base)||(base > z->base+z->count))
|
|
return;
|
|
|
|
if(base < z->base)
|
|
base = z->base;
|
|
|
|
if(top > z->base+z->count)
|
|
top = z->base+z->count;
|
|
|
|
DBG("zone reserve base %x top %x\n", base, top);
|
|
|
|
for (i = base; i < top; i++)
|
|
zone_mark_unavailable(z, i - z->base);
|
|
|
|
};
|
|
|
|
static void zone_release(zone_t *z, pfn_t base, count_t count)
|
|
{
|
|
int i;
|
|
pfn_t top = base+count;
|
|
|
|
if( (base+count < z->base)||(base > z->base+z->count))
|
|
return;
|
|
|
|
if(base < z->base)
|
|
base = z->base;
|
|
|
|
if(top > z->base+z->count)
|
|
top = z->base+z->count;
|
|
|
|
DBG("zone release base %x top %x\n", base, top);
|
|
|
|
for (i = base; i < top; i++) {
|
|
z->frames[i-z->base].refcount = 0;
|
|
buddy_system_free(&z->frames[i-z->base].buddy_link);
|
|
}
|
|
};
|
|
|
|
static inline frame_t * zone_get_frame(zone_t *zone, index_t frame_idx)
|
|
{
|
|
ASSERT(frame_idx < zone->count);
|
|
return &zone->frames[frame_idx];
|
|
}
|
|
|
|
void __fastcall frame_set_parent(pfn_t pfn, void *data)
|
|
{
|
|
spinlock_lock(&z_core.lock);
|
|
zone_get_frame(&z_core, pfn-z_core.base)->parent = data;
|
|
spinlock_unlock(&z_core.lock);
|
|
}
|
|
|
|
void* __fastcall frame_get_parent(pfn_t pfn)
|
|
{
|
|
void *res;
|
|
|
|
spinlock_lock(&z_core.lock);
|
|
res = zone_get_frame(&z_core, pfn)->parent;
|
|
spinlock_unlock(&z_core.lock);
|
|
|
|
return res;
|
|
}
|
|
|