-
Notifications
You must be signed in to change notification settings - Fork 264
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add logic to stack lcow layers on a single VPMEM device
Signed-off-by: Maksim An <maksiman@microsoft.com>
- Loading branch information
Showing
9 changed files
with
735 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* HCS API | ||
* | ||
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) | ||
* | ||
* API version: 2.4 | ||
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) | ||
*/ | ||
|
||
package hcsschema | ||
|
||
type VirtualPMemMapping struct { | ||
HostPath string `json:"HostPath,omitempty"` | ||
ImageFormat string `json:"ImageFormat,omitempty"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package uvm | ||
|
||
import ( | ||
"github.com/pkg/errors" | ||
) | ||
|
||
var ( | ||
ErrInvalidMemoryClass = errors.New("invalid memory class") | ||
) | ||
|
||
type memSlot struct { | ||
memCls uint32 | ||
offset uint64 | ||
size uint64 | ||
next *memSlot | ||
} | ||
|
||
type memoryAllocator struct { | ||
slots [MemClsNum]*memSlot | ||
busy map[uint64]*memSlot | ||
} | ||
|
||
// newDefaultMemoryAllocator returns an allocator with a single 0-offset 4GB memory slot | ||
func newDefaultMemoryAllocator() memoryAllocator { | ||
mem := memoryAllocator{ | ||
busy: make(map[uint64]*memSlot), | ||
} | ||
mem.slots[MemClsNum-1] = &memSlot{ | ||
offset: 0, | ||
size: 4 * 1024 * MegaByte, | ||
} | ||
return mem | ||
} | ||
|
||
func (mc *memoryAllocator) findFreeOffset(memCls uint32) (uint64, error) { | ||
memClsCount := uint32(len(mc.slots)) | ||
if memCls >= MemClsNum { | ||
return 0, ErrInvalidMemoryClass | ||
} | ||
|
||
for i := memCls; i < memClsCount; i++ { | ||
if ms := mc.slots[i]; ms != nil { | ||
return ms.offset, nil | ||
} | ||
} | ||
return 0, ErrNotEnoughSpace | ||
} | ||
|
||
// allocate returns first available memory slot for a given `memCls` size class. If none left, | ||
// expands the existing memCls | ||
func (mc *memoryAllocator) allocate(memCls uint32) (*memSlot, error) { | ||
if memCls >= MemClsNum { | ||
return nil, ErrInvalidMemoryClass | ||
} | ||
|
||
alloc := func() *memSlot { | ||
s := mc.slots[memCls] | ||
if s != nil { | ||
mc.slots[memCls] = s.next | ||
s.next = nil | ||
mc.busy[s.offset] = s | ||
return s | ||
} | ||
return nil | ||
} | ||
|
||
if slot := alloc(); slot != nil { | ||
return slot, nil | ||
} | ||
|
||
if err := mc.expand(memCls); err != nil { | ||
return nil, err | ||
} | ||
|
||
slot := alloc() | ||
if slot != nil { | ||
return slot, nil | ||
} | ||
|
||
return nil, ErrNotEnoughSpace | ||
} | ||
|
||
// release returns resources back, also making sure that the `offset` and `memCls` are valid | ||
func (mc *memoryAllocator) release(memCls uint32, offset uint64) error { | ||
ms, ok := mc.busy[offset] | ||
if !ok { | ||
return errors.Errorf("no memory allocated at offset: %d", offset) | ||
} | ||
|
||
if ms == nil { | ||
return errors.Errorf("expected memory slot at offset: %d, found nil instead", offset) | ||
} | ||
|
||
if memCls != ms.memCls { | ||
return errors.Errorf("invalid release request. memory slot types mismatch: actual=%d, requested=%d", ms.memCls, memCls) | ||
} | ||
|
||
delete(mc.busy, offset) | ||
ms.next = mc.slots[ms.memCls] | ||
mc.slots[ms.memCls] = ms | ||
return nil | ||
} | ||
|
||
// expand breaks down the next memory class into smaller memCls type segments | ||
func (mc *memoryAllocator) expand(memCls uint32) error { | ||
nextMemCls := memCls + 1 | ||
if nextMemCls >= MemClsNum { | ||
return ErrNotEnoughSpace | ||
} | ||
|
||
if mc.slots[nextMemCls] == nil { | ||
err := mc.expand(nextMemCls) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
nextSlot := mc.slots[nextMemCls] | ||
mc.slots[nextMemCls] = nextSlot.next | ||
nextSlot.next = nil | ||
|
||
offset := nextSlot.offset | ||
memClsSize := getMemoryClassSize(memCls) | ||
for i := uint64(0); i < 4; i++ { | ||
n := &memSlot{ | ||
size: memClsSize, | ||
offset: offset + (3-i)*memClsSize, | ||
memCls: memCls, | ||
} | ||
n.next = mc.slots[memCls] | ||
mc.slots[memCls] = n | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package uvm | ||
|
||
import "testing" | ||
|
||
func testAllocate(t *testing.T, ma *memoryAllocator, memCls uint32, expectedOffset uint64) { | ||
s, err := ma.allocate(memCls) | ||
if err != nil { | ||
t.Errorf("unexpected error: %s", err) | ||
} | ||
if s.offset != expectedOffset { | ||
t.Errorf("wrong offset, expected offset=%d, actual offset=%d", expectedOffset, s.offset) | ||
} | ||
if len(ma.busy) != 1 { | ||
t.Error("memory slot wasn't marked as busy") | ||
} | ||
} | ||
|
||
func Test_MemAlloc_findFreeOffset(t *testing.T) { | ||
ma := newDefaultMemoryAllocator() | ||
offset, err := ma.findFreeOffset(0) | ||
if err != nil { | ||
t.Errorf("unexpected error: %s", err) | ||
} | ||
|
||
if offset != 0 { | ||
t.Errorf("expected offset=%d, got %d", 0, offset) | ||
} | ||
} | ||
|
||
func Test_MemAlloc_allocate_without_expand(t *testing.T) { | ||
ma := &memoryAllocator{ | ||
busy: make(map[uint64]*memSlot), | ||
} | ||
ma.slots[0] = &memSlot{ | ||
offset: 0, | ||
size: MegaByte, | ||
memCls: 0, | ||
} | ||
|
||
testAllocate(t, ma, 0, 0) | ||
} | ||
|
||
func Test_MemAlloc_allocate_not_enough_space(t *testing.T) { | ||
ma := memoryAllocator{ | ||
busy: make(map[uint64]*memSlot), | ||
} | ||
|
||
_, err := ma.allocate(0) | ||
if err == nil { | ||
t.Error("expected error, got nil") | ||
} | ||
if err != ErrNotEnoughSpace { | ||
t.Errorf("expected error=%s, got error=%s", ErrNotEnoughSpace, err) | ||
} | ||
} | ||
|
||
func Test_MemAlloc_expand(t *testing.T) { | ||
ma := &memoryAllocator{ | ||
busy: make(map[uint64]*memSlot), | ||
} | ||
ma.slots[1] = &memSlot{ | ||
offset: 0, | ||
size: 4 * MegaByte, | ||
memCls: 1, | ||
} | ||
|
||
err := ma.expand(0) | ||
if err != nil { | ||
t.Errorf("unexpected error: %s", err) | ||
} | ||
|
||
if ma.slots[1] != nil { | ||
t.Error("slot 1 was not broken into smaller pieces") | ||
} | ||
|
||
s := ma.slots[0] | ||
for i := 0; i < 4; i++ { | ||
expected := uint64(i) * MegaByte | ||
if s.offset != expected { | ||
t.Errorf("unexpected expand. expected offset=%d, got offset=%d", expected, s.offset) | ||
} | ||
s = s.next | ||
} | ||
|
||
if s != nil { | ||
t.Errorf("extra memory slots: %v", s) | ||
} | ||
} | ||
|
||
func Test_MemAlloc_allocate_automatically_expands(t *testing.T) { | ||
ma := memoryAllocator{ | ||
busy: make(map[uint64]*memSlot), | ||
} | ||
ma.slots[2] = &memSlot{ | ||
offset: MegaByte, | ||
size: 16 * MegaByte, | ||
memCls: 2, | ||
} | ||
|
||
testAllocate(t, &ma, 0, MegaByte) | ||
|
||
if ma.slots[1] == nil { | ||
t.Error("memory not extended for the memCls 1") | ||
} | ||
if ma.slots[1].offset != 5*MegaByte { | ||
t.Error("wrong offset for the memCls 1") | ||
} | ||
if ma.slots[2] != nil { | ||
t.Error("expected to find nil") | ||
} | ||
} | ||
|
||
func Test_MemAlloc_alloc_and_release(t *testing.T) { | ||
ma := &memoryAllocator{ | ||
busy: make(map[uint64]*memSlot), | ||
} | ||
ma.slots[0] = &memSlot{ | ||
offset: 0, | ||
size: MegaByte, | ||
memCls: 0, | ||
} | ||
|
||
testAllocate(t, ma, 0, 0) | ||
|
||
err := ma.release(0, 0) | ||
if err != nil { | ||
t.Errorf("error releasing resources: %s", err) | ||
} | ||
if len(ma.busy) != 0 { | ||
t.Errorf("resources not marked as free: %v", ma.busy) | ||
} | ||
if ma.slots[0] == nil || ma.slots[0].offset != 0 { | ||
t.Error("resource not assigned back to the free list") | ||
} | ||
} | ||
|
||
func Test_MemAlloc_alloc_invalid_class(t *testing.T) { | ||
ma := &memoryAllocator{} | ||
|
||
_, err := ma.allocate(MemClsNum) | ||
if err == nil { | ||
t.Error("no error returned") | ||
} | ||
if err != ErrInvalidMemoryClass { | ||
t.Errorf("expected error=%s, got error=%s", ErrInvalidMemoryClass, err) | ||
} | ||
} |
Oops, something went wrong.