1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Implement AllocFrame/FreeFrame

This commit is contained in:
Achilleas Anagnostopoulos 2017-06-18 08:53:55 +01:00
parent 6ca86e55f8
commit 4de2d54ed4
2 changed files with 133 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package allocator
import (
"math"
"reflect"
"unsafe"
@ -17,6 +18,10 @@ var (
// primary allocator for reserving pages.
FrameAllocator BitmapAllocator
errBitmapAllocOutOfMemory = &kernel.Error{Module: "bitmap_alloc", Message: "out of memory"}
errBitmapAllocFrameNotManaged = &kernel.Error{Module: "bitmap_alloc", Message: "frame not managed by this allocator"}
errBitmapAllocDoubleFree = &kernel.Error{Module: "bitmap_alloc", Message: "frame is already free"}
// The followning functions are used by tests to mock calls to the vmm package
// and are automatically inlined by the compiler.
reserveRegionFn = vmm.EarlyReserveRegion
@ -238,6 +243,60 @@ func (alloc *BitmapAllocator) printStats() {
)
}
// AllocFrame reserves and returns a physical memory frame. An error will be
// returned if no more memory can be allocated.
func (alloc *BitmapAllocator) AllocFrame() (pmm.Frame, *kernel.Error) {
for poolIndex := 0; poolIndex < len(alloc.pools); poolIndex++ {
if alloc.pools[poolIndex].freeCount == 0 {
continue
}
fullBlock := uint64(math.MaxUint64)
for blockIndex, block := range alloc.pools[poolIndex].freeBitmap {
if block == fullBlock {
continue
}
// Block has at least one free slot; we need to scan its bits
for blockOffset, mask := 0, uint64(1<<63); mask > 0; blockOffset, mask = blockOffset+1, mask>>1 {
if block&mask != 0 {
continue
}
alloc.pools[poolIndex].freeCount--
alloc.pools[poolIndex].freeBitmap[blockIndex] |= mask
alloc.reservedPages++
return alloc.pools[poolIndex].startFrame + pmm.Frame((blockIndex<<6)+blockOffset), nil
}
}
}
return pmm.InvalidFrame, errBitmapAllocOutOfMemory
}
// FreeFrame releases a frame previously allocated via a call to AllocFrame.
// Trying to release a frame not part of the allocator pools or a frame that
// is already marked as free will cause an error to be returned.
func (alloc *BitmapAllocator) FreeFrame(frame pmm.Frame) *kernel.Error {
poolIndex := alloc.poolForFrame(frame)
if poolIndex < 0 {
return errBitmapAllocFrameNotManaged
}
relFrame := frame - alloc.pools[poolIndex].startFrame
block := relFrame >> 6
mask := uint64(1 << (63 - (relFrame - block<<6)))
if alloc.pools[poolIndex].freeBitmap[block]&mask == 0 {
return errBitmapAllocDoubleFree
}
alloc.pools[poolIndex].freeBitmap[block] &^= mask
alloc.pools[poolIndex].freeCount++
alloc.reservedPages--
return nil
}
// earlyAllocFrame is a helper that delegates a frame allocation request to the
// early allocator instance. This function is passed as an argument to
// vmm.SetFrameAllocator instead of earlyAllocator.AllocFrame. The latter

View File

@ -313,6 +313,80 @@ func TestBitmapAllocatorReserveEarlyAllocatorFrames(t *testing.T) {
}
}
func TestBitmapAllocatorAllocAndFreeFrame(t *testing.T) {
var alloc = BitmapAllocator{
pools: []framePool{
{
startFrame: pmm.Frame(0),
endFrame: pmm.Frame(7),
freeCount: 8,
// only the first 8 bits of block 0 are used
freeBitmap: make([]uint64, 1),
},
{
startFrame: pmm.Frame(64),
endFrame: pmm.Frame(191),
freeCount: 128,
freeBitmap: make([]uint64, 2),
},
},
totalPages: 136,
}
// Test Alloc
for poolIndex, pool := range alloc.pools {
for expFrame := pool.startFrame; expFrame <= pool.endFrame; expFrame++ {
got, err := alloc.AllocFrame()
if err != nil {
t.Fatalf("[pool %d] unexpected error: %v", err)
}
if got != expFrame {
t.Errorf("[pool %d] expected allocated frame to be %d; got %d", poolIndex, expFrame, got)
}
}
if alloc.pools[poolIndex].freeCount != 0 {
t.Errorf("[pool %d] expected free count to be 0; got %d", poolIndex, alloc.pools[poolIndex].freeCount)
}
}
if alloc.reservedPages != alloc.totalPages {
t.Errorf("expected reservedPages to match totalPages(%d); got %d", alloc.totalPages, alloc.reservedPages)
}
if _, err := alloc.AllocFrame(); err != errBitmapAllocOutOfMemory {
t.Fatalf("expected error errBitmapAllocOutOfMemory; got %v", err)
}
// Test Free
expFreeCount := []uint32{8, 128}
for poolIndex, pool := range alloc.pools {
for frame := pool.startFrame; frame <= pool.endFrame; frame++ {
if err := alloc.FreeFrame(frame); err != nil {
t.Fatalf("[pool %d] unexpected error: %v", err)
}
}
if alloc.pools[poolIndex].freeCount != expFreeCount[poolIndex] {
t.Errorf("[pool %d] expected free count to be %d; got %d", poolIndex, expFreeCount[poolIndex], alloc.pools[poolIndex].freeCount)
}
}
if alloc.reservedPages != 0 {
t.Errorf("expected reservedPages to be 0; got %d", alloc.reservedPages)
}
// Test Free errors
if err := alloc.FreeFrame(pmm.Frame(0)); err != errBitmapAllocDoubleFree {
t.Fatalf("expected error errBitmapAllocDoubleFree; got %v", err)
}
if err := alloc.FreeFrame(pmm.Frame(0xbadf00d)); err != errBitmapAllocFrameNotManaged {
t.Fatalf("expected error errBitmapFrameNotManaged; got %v", err)
}
}
func TestAllocatorPackageInit(t *testing.T) {
defer func() {
mapFn = vmm.Map