161 lines
3.8 KiB
C++
161 lines
3.8 KiB
C++
/*
|
|
* Slab Allocator
|
|
*
|
|
* Copyright (C) 2007-2009, Udo Steinberg <udo@hypervisor.org>
|
|
*
|
|
* This file is part of the NOVA microhypervisor.
|
|
*
|
|
* NOVA is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* NOVA is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License version 2 for more details.
|
|
*/
|
|
|
|
#include "assert.h"
|
|
#include "lock_guard.h"
|
|
#include "slab.h"
|
|
#include "stdio.h"
|
|
|
|
Slab::Slab (Slab_cache *slab_cache)
|
|
: avail (slab_cache->elem),
|
|
cache (slab_cache),
|
|
prev (0),
|
|
next (0),
|
|
head (0)
|
|
{
|
|
char *link = reinterpret_cast<char *>(this) + PAGE_SIZE - cache->buff + cache->size;
|
|
|
|
for (unsigned long i = avail; i; i--, link -= cache->buff) {
|
|
*reinterpret_cast<char **>(link) = head;
|
|
head = link;
|
|
}
|
|
}
|
|
|
|
void *Slab::alloc()
|
|
{
|
|
avail--;
|
|
|
|
void *link = reinterpret_cast<void *>(head - cache->size);
|
|
head = *reinterpret_cast<char **>(head);
|
|
return link;
|
|
}
|
|
|
|
void Slab::free (void *ptr)
|
|
{
|
|
avail++;
|
|
|
|
char *link = reinterpret_cast<char *>(ptr) + cache->size;
|
|
*reinterpret_cast<char **>(link) = head;
|
|
head = link;
|
|
}
|
|
|
|
Slab_cache::Slab_cache (unsigned long elem_size, unsigned elem_align)
|
|
: curr (0),
|
|
head (0),
|
|
size (align_up (elem_size, sizeof (mword))),
|
|
buff (align_up (size + sizeof (mword), elem_align)),
|
|
elem ((PAGE_SIZE - sizeof (Slab)) / buff)
|
|
{
|
|
trace (TRACE_MEMORY, "Slab Cache:%p (S:%lu A:%u)",
|
|
this,
|
|
elem_size,
|
|
elem_align);
|
|
}
|
|
|
|
void Slab_cache::grow()
|
|
{
|
|
Slab *slab = new Slab (this);
|
|
|
|
// Prepend to list
|
|
slab->next = head;
|
|
head = curr = slab;
|
|
}
|
|
|
|
void Slab_cache::reap()
|
|
{
|
|
Slab *s;
|
|
for (Slab *slab = head; slab; s = slab, slab = slab->next, delete s) ;
|
|
}
|
|
|
|
void *Slab_cache::alloc()
|
|
{
|
|
Lock_guard <Spinlock> guard (lock);
|
|
|
|
if (EXPECT_FALSE (!curr))
|
|
grow();
|
|
|
|
assert (!curr->full());
|
|
assert (!curr->next || curr->next->full());
|
|
|
|
// Allocate from slab
|
|
void *ret = curr->alloc();
|
|
|
|
if (EXPECT_FALSE (curr->full()))
|
|
curr = curr->prev;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void Slab_cache::free (void *ptr)
|
|
{
|
|
Lock_guard <Spinlock> guard (lock);
|
|
|
|
Slab *slab = reinterpret_cast<Slab *>(reinterpret_cast<mword>(ptr) & ~PAGE_MASK);
|
|
|
|
bool was_full = slab->full();
|
|
|
|
slab->free (ptr); // Deallocate from slab
|
|
|
|
if (EXPECT_FALSE (was_full)) {
|
|
|
|
// There are full slabs in front of us and we're partial; requeue
|
|
if (slab->prev && slab->prev->full()) {
|
|
|
|
// Dequeue
|
|
slab->prev->next = slab->next;
|
|
if (slab->next)
|
|
slab->next->prev = slab->prev;
|
|
|
|
// Enqueue after curr
|
|
if (curr) {
|
|
slab->prev = curr;
|
|
slab->next = curr->next;
|
|
curr->next = curr->next->prev = slab;
|
|
}
|
|
|
|
// Enqueue as head
|
|
else {
|
|
slab->prev = 0;
|
|
slab->next = head;
|
|
head = head->prev = slab;
|
|
}
|
|
}
|
|
|
|
curr = slab;
|
|
|
|
} else if (EXPECT_FALSE (slab->empty())) {
|
|
|
|
// There are partial slabs in front of us and we're empty; requeue
|
|
if (slab->prev && !slab->prev->empty()) {
|
|
|
|
// Make partial slab in front of us current if we were current
|
|
if (slab == curr)
|
|
curr = slab->prev;
|
|
|
|
// Dequeue
|
|
slab->prev->next = slab->next;
|
|
if (slab->next)
|
|
slab->next->prev = slab->prev;
|
|
|
|
// Enqueue as head
|
|
slab->prev = 0;
|
|
slab->next = head;
|
|
head = head->prev = slab;
|
|
}
|
|
}
|
|
}
|