Skip to content

Commit

Permalink
add logic to stack lcow layers on a single VPMEM device
Browse files Browse the repository at this point in the history
Signed-off-by: Maksim An <maksiman@microsoft.com>
  • Loading branch information
anmaxvl committed Feb 4, 2021
1 parent 251b969 commit 710fdcb
Show file tree
Hide file tree
Showing 10 changed files with 877 additions and 113 deletions.
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de h1:dlfGmNcE3jDAec
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
Expand All @@ -32,6 +33,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
Expand Down
8 changes: 8 additions & 0 deletions internal/guestrequest/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ type LCOWMappedDirectory struct {
ReadOnly bool `json:"ReadOnly,omitempty"`
}

// One of potentially multiple read-only layers mapped on a VPMem device
type LCOWMappedLayer struct {
DeviceOffsetInBytes uint64 `json:"DeviceOffsetInBytes,omitempty"`
DeviceSizeInBytes uint64 `json:"DeviceSizeInBytes,omitempty"`
}

// Read-only layers over VPMem
type LCOWMappedVPMemDevice struct {
DeviceNumber uint32 `json:"DeviceNumber,omitempty"`
MountPath string `json:"MountPath,omitempty"`
// Mapping is ignored when MountPath is not empty
MappingInfo LCOWMappedLayer `json:"MappingInfo,omitempty"`
}

type LCOWMappedVPCIDevice struct {
Expand Down
12 changes: 6 additions & 6 deletions internal/schema2/virtual_p_mem_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
*
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
* API version: 2.1
* API version: 2.4
* Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
*/

package hcsschema

type VirtualPMemDevice struct {
HostPath string `json:"HostPath,omitempty"`

ReadOnly bool `json:"ReadOnly,omitempty"`

ImageFormat string `json:"ImageFormat,omitempty"`
HostPath string `json:"HostPath,omitempty"`
ReadOnly bool `json:"ReadOnly,omitempty"`
ImageFormat string `json:"ImageFormat,omitempty"`
SizeBytes int32 `json:"SizeBytes,omitempty"`
Mappings map[string]VirtualPMemMapping `json:"Mappings,omitempty"`
}
15 changes: 15 additions & 0 deletions internal/schema2/virtual_p_mem_mapping.go
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"`
}
13 changes: 9 additions & 4 deletions internal/uvm/create_lcow.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,16 @@ func CreateLCOW(ctx context.Context, opts *OptionsLCOW) (_ *UtilityVM, err error
},
}
// Add to our internal structure
uvm.vpmemDevices[0] = &vpmemInfo{
hostPath: opts.RootFSFile,
uvmPath: "/",
refCount: 1,
pMemDev := newVPMemDevice()
mapping := &vpmemMapping{
hostPath: opts.RootFSFile,
uvmPath: "/",
refCount: 1,
deviceOffset: 0,
deviceSize: DefaultVPMemSizeBytes,
}
pMemDev.mapDevice(ctx, mapping)
uvm.vpmemDevices[0] = pMemDev
}

vmDebugging := false
Expand Down
182 changes: 182 additions & 0 deletions internal/uvm/mem_allocator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package uvm

import (
"github.com/pkg/errors"
)

const (
MemCls1MB uint32 = iota
MemCls4MB
MemCls16MB
MemCls64MB
MemCls256MB
MemCls1GB
MemCls4GB
)

const (
MegaByte = 1024 * 1024
MemClsNum = MemCls4GB + 1
)

var (
ErrInvalidMemoryClass = errors.New("invalid memory class")
)

// getMemoryClassType returns memory class for a given size. The smallest class is 1MB and the
// largest one is 4GB with 2 bit offset intervals in between, for a total of 7 different classes
func getMemoryClassType(s uint64) uint32 {
s = s >> 20
memCls := uint32(0)
for s > 1 {
s = s >> 2
memCls++
}
return memCls
}

// getMemoryClassSize returns size in bytes for a given memory class
func getMemoryClassSize(memCls uint32) uint64 {
switch memCls {
case MemCls1MB:
return 1 * MegaByte
case MemCls4MB:
return 4 * MegaByte
case MemCls16MB:
return 16 * MegaByte
case MemCls64MB:
return 64 * MegaByte
case MemCls256MB:
return 256 * MegaByte
case MemCls1GB:
return 1024 * MegaByte
case MemCls4GB:
return 4 * 1024 * MegaByte
}
return 0
}

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
}
Loading

0 comments on commit 710fdcb

Please sign in to comment.