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:
parent
6ca86e55f8
commit
4de2d54ed4
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user