#ifndef SDI2_PAGER_BUDDYSYSTEM_H
#define SDI2_PAGER_BUDDYSYSTEM_H
#include "PageFreeList.h"
#include "pdebug.h"
class BuddySystem
{
public:
static const unsigned int minpagesizelog2 = 12;
static const unsigned int maxpagesizelog2 = 24;
static const unsigned int freelistnum = maxpagesizelog2 - minpagesizelog2 + 1;
static const unsigned int minpagesize = (1 << minpagesizelog2);
static const unsigned int maxpagesize = (1 << maxpagesizelog2);
private:
typedef SlabAllocator<PageFreeEntry> slaballoc_t;
slaballoc_t::Pool& slaballoc;
PageFreeList freelist[freelistnum];
L4_Word_t freemem;
static inline unsigned int listpagesize(unsigned int listnum)
{
return 1 << (minpagesizelog2 + listnum);
}
static inline L4_Fpage_t calcBuddyPage(L4_Fpage_t p)
{
p.raw ^= (1 << p.X.s);
return p;
}
public:
explicit inline BuddySystem(slaballoc_t::Pool &usedallocator)
: slaballoc(usedallocator), freemem(0)
{
for(unsigned int i = 0; i < freelistnum; i++)
freelist[i].setSlabAllocator(slaballoc);
}
inline L4_Word_t available() const
{ return freemem; }
inline unsigned int get_freelistnum() const
{ return freelistnum; }
inline const PageFreeList* get_freelist(unsigned int n) const
{ return &freelist[n]; }
inline void test() const
{
L4_Word_t totmem = 0;
for(unsigned int i = 0; i < freelistnum; i++)
{
const struct PageFreeEntry *pfe = freelist[i].begin();
if (!pfe) continue;
while(1)
{
assert(L4_Size(pfe->page) == listpagesize(i));
totmem += L4_Size(pfe->page);
if (pfe->next == pfe) break;
pfe = pfe->next;
}
}
assert(totmem == freemem);
}
inline L4_Fpage_t allocateLog2(unsigned int pagesizelog2)
{
if (pagesizelog2 > maxpagesizelog2) {
assert(0);
return L4_Nilpage;
}
if (pagesizelog2 < minpagesizelog2)
pagesizelog2 = minpagesizelog2;
pdbg2("pager: buddysys allocating page: %lu bytes\n", (1UL << pagesizelog2));
test();
unsigned int fl = pagesizelog2 - minpagesizelog2;
L4_Fpage_t p = freelist[fl].getFirstPage();
if (L4_IsNilFpage(p))
{
if (pagesizelog2 == maxpagesizelog2) {
return L4_Nilpage;
}
p = allocateLog2(pagesizelog2 + 1);
if (L4_IsNilFpage(p)) {
return p;
}
p.X.s--;
L4_Fpage_t buddy = calcBuddyPage(p);
insert(buddy);
}
else
{
freemem -= L4_Size(p);
}
test();
return p;
}
inline L4_Fpage_t allocate(unsigned int pagesize)
{
unsigned int pslog2 = __L4_Msb(pagesize);
if ((1UL << pslog2) < pagesize) pslog2++;
return allocateLog2(pslog2);
}
inline void insert(L4_Fpage_t page)
{
pdbg3("pager: buddysys insert 0x%lx - size %lu\n",
L4_Address(page), L4_Size(page));
test();
unsigned int sl = L4_SizeLog2(page);
if (sl < minpagesizelog2) {
assert(0);
return;
}
if (sl > maxpagesizelog2)
{
assert(0);
return;
}
if (sl == maxpagesizelog2)
{
freelist[maxpagesizelog2 - minpagesizelog2].insert(page);
freemem += L4_Size(page);
}
else
{
unsigned int fl = sl - minpagesizelog2;
PageFreeEntry *nearest = freelist[fl].findNearest(page);
L4_Fpage_t buddy = calcBuddyPage(page);
if (nearest && nearest->page.raw == buddy.raw)
{
pdbg3("Found greater buddy.\n");
freelist[fl].remove(nearest);
freemem -= L4_Size(page);
insert(L4_FpageLog2(L4_Address(page) & ~(1UL << sl), sl+1));
}
else if (nearest && nearest->prev->page.raw == buddy.raw)
{
pdbg3("Found smaller buddy.\n");
freelist[fl].remove(nearest->prev);
freemem -= L4_Size(page);
insert(L4_FpageLog2(L4_Address(page) & ~(1UL << sl), sl+1));
}
else {
freelist[fl].insertNear(page, nearest);
freemem += L4_Size(page);
}
}
test();
}
inline void dump() const
{
printf("Buddysystem dump:\n");
for(unsigned int fl = freelistnum; fl > 0; fl--)
{
if (freelist[fl-1].begin() == NULL) continue;
pdbg0("Pages size %d\n", listpagesize(fl-1));
freelist[fl-1].dump();
}
}
};
#endif