mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-20 13:06:38 +00:00
Migrate to containerd v1.7.0 and update dependencies
* Updates containerd to v1.7.0 and new binary for 32-bit Arm OSes. * Updates Go dependencies - openfaas and external Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
9efd019e86
commit
c41c2cd9fc
316
vendor/github.com/Microsoft/hcsshim/internal/memory/pool.go
generated
vendored
Normal file
316
vendor/github.com/Microsoft/hcsshim/internal/memory/pool.go
generated
vendored
Normal file
@ -0,0 +1,316 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
minimumClassSize = MiB
|
||||
maximumClassSize = 4 * GiB
|
||||
memoryClassNumber = 7
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidMemoryClass = errors.New("invalid memory class")
|
||||
ErrEarlyMerge = errors.New("not all children have been freed")
|
||||
ErrEmptyPoolOperation = errors.New("operation on empty pool")
|
||||
)
|
||||
|
||||
// GetMemoryClassType returns the minimum memory class type that can hold a device of
|
||||
// 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. This function does not
|
||||
// do a validity check
|
||||
func GetMemoryClassType(s uint64) classType {
|
||||
s = (s - 1) >> 20
|
||||
memCls := uint32(0)
|
||||
for s > 0 {
|
||||
s = s >> 2
|
||||
memCls++
|
||||
}
|
||||
return classType(memCls)
|
||||
}
|
||||
|
||||
// GetMemoryClassSize returns size in bytes for a given memory class
|
||||
func GetMemoryClassSize(memCls classType) (uint64, error) {
|
||||
if memCls >= memoryClassNumber {
|
||||
return 0, ErrInvalidMemoryClass
|
||||
}
|
||||
return minimumClassSize << (2 * memCls), nil
|
||||
}
|
||||
|
||||
// region represents a contiguous memory block
|
||||
type region struct {
|
||||
// parent region that has been split into 4
|
||||
parent *region
|
||||
class classType
|
||||
// offset represents offset in bytes
|
||||
offset uint64
|
||||
}
|
||||
|
||||
// memoryPool tracks free and busy (used) memory regions
|
||||
type memoryPool struct {
|
||||
free map[uint64]*region
|
||||
busy map[uint64]*region
|
||||
}
|
||||
|
||||
// PoolAllocator implements a memory allocation strategy similar to buddy-malloc https://github.com/evanw/buddy-malloc/blob/master/buddy-malloc.c
|
||||
// We borrow the idea of spanning a tree of fixed size regions on top of a contiguous memory
|
||||
// space.
|
||||
//
|
||||
// There are a total of 7 different region sizes that can be allocated, with the smallest
|
||||
// being 1MB and the largest 4GB (the default maximum size of a Virtual PMem device).
|
||||
//
|
||||
// For efficiency and to reduce fragmentation an entire region is allocated when requested.
|
||||
// When there's no available region of requested size, we try to allocate more memory for
|
||||
// this particular size by splitting the next available larger region into smaller ones, e.g.
|
||||
// if there's no region available for size class 0, we try splitting a region from class 1,
|
||||
// then class 2 etc, until we are able to do so or hit the upper limit.
|
||||
type PoolAllocator struct {
|
||||
pools [memoryClassNumber]*memoryPool
|
||||
}
|
||||
|
||||
var _ MappedRegion = ®ion{}
|
||||
var _ Allocator = &PoolAllocator{}
|
||||
|
||||
func (r *region) Offset() uint64 {
|
||||
return r.offset
|
||||
}
|
||||
|
||||
func (r *region) Size() uint64 {
|
||||
sz, err := GetMemoryClassSize(r.class)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sz
|
||||
}
|
||||
|
||||
func (r *region) Type() classType {
|
||||
return r.class
|
||||
}
|
||||
|
||||
func newEmptyMemoryPool() *memoryPool {
|
||||
return &memoryPool{
|
||||
free: make(map[uint64]*region),
|
||||
busy: make(map[uint64]*region),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPoolMemoryAllocator() PoolAllocator {
|
||||
pa := PoolAllocator{}
|
||||
p := newEmptyMemoryPool()
|
||||
// by default we allocate a single region with maximum possible size (class type)
|
||||
p.free[0] = ®ion{
|
||||
class: memoryClassNumber - 1,
|
||||
offset: 0,
|
||||
}
|
||||
pa.pools[memoryClassNumber-1] = p
|
||||
return pa
|
||||
}
|
||||
|
||||
// Allocate checks memory region pool for the given `size` and returns a free region with
|
||||
// minimal offset, if none available tries expanding matched memory pool.
|
||||
//
|
||||
// Internally it's done via moving a region from free pool into a busy pool
|
||||
func (pa *PoolAllocator) Allocate(size uint64) (MappedRegion, error) {
|
||||
memCls := GetMemoryClassType(size)
|
||||
if memCls >= memoryClassNumber {
|
||||
return nil, ErrInvalidMemoryClass
|
||||
}
|
||||
|
||||
// find region with the smallest offset
|
||||
nextCls, nextOffset, err := pa.findNextOffset(memCls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// this means that there are no more regions for the current class, try expanding
|
||||
if nextCls != memCls {
|
||||
if err := pa.split(memCls); err != nil {
|
||||
if err == ErrInvalidMemoryClass {
|
||||
return nil, ErrNotEnoughSpace
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := pa.markBusy(memCls, nextOffset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// by this point memory pool for memCls should have been created,
|
||||
// either prior or during split call
|
||||
if r := pa.pools[memCls].busy[nextOffset]; r != nil {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
return nil, ErrNotEnoughSpace
|
||||
}
|
||||
|
||||
// Release marks a memory region of class `memCls` and offset `offset` as free and tries to merge smaller regions into
|
||||
// a bigger one
|
||||
func (pa *PoolAllocator) Release(reg MappedRegion) error {
|
||||
mp := pa.pools[reg.Type()]
|
||||
if mp == nil {
|
||||
return ErrEmptyPoolOperation
|
||||
}
|
||||
|
||||
err := pa.markFree(reg.Type(), reg.Offset())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n := mp.free[reg.Offset()]
|
||||
if n == nil {
|
||||
return ErrNotAllocated
|
||||
}
|
||||
if err := pa.merge(n.parent); err != nil {
|
||||
if err != ErrEarlyMerge {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findNextOffset finds next region location for a given memCls
|
||||
func (pa *PoolAllocator) findNextOffset(memCls classType) (classType, uint64, error) {
|
||||
for mc := memCls; mc < memoryClassNumber; mc++ {
|
||||
pi := pa.pools[mc]
|
||||
if pi == nil || len(pi.free) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
target := uint64(maximumClassSize)
|
||||
for offset := range pi.free {
|
||||
if offset < target {
|
||||
target = offset
|
||||
}
|
||||
}
|
||||
return mc, target, nil
|
||||
}
|
||||
return 0, 0, ErrNotEnoughSpace
|
||||
}
|
||||
|
||||
// split tries to recursively split a bigger memory region into smaller ones until it succeeds or hits the upper limit
|
||||
func (pa *PoolAllocator) split(clsType classType) error {
|
||||
nextClsType := clsType + 1
|
||||
if nextClsType >= memoryClassNumber {
|
||||
return ErrInvalidMemoryClass
|
||||
}
|
||||
|
||||
nextPool := pa.pools[nextClsType]
|
||||
if nextPool == nil {
|
||||
nextPool = newEmptyMemoryPool()
|
||||
pa.pools[nextClsType] = nextPool
|
||||
}
|
||||
|
||||
cls, offset, err := pa.findNextOffset(nextClsType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// not enough memory in the next class, try to recursively expand
|
||||
if cls != nextClsType {
|
||||
if err := pa.split(nextClsType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := pa.markBusy(nextClsType, offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// memCls validity has been checked already, we can ignore the error
|
||||
clsSize, _ := GetMemoryClassSize(clsType)
|
||||
|
||||
nextReg := nextPool.busy[offset]
|
||||
if nextReg == nil {
|
||||
return ErrNotAllocated
|
||||
}
|
||||
|
||||
// expand memCls
|
||||
cp := pa.pools[clsType]
|
||||
if cp == nil {
|
||||
cp = newEmptyMemoryPool()
|
||||
pa.pools[clsType] = cp
|
||||
}
|
||||
// create 4 smaller regions
|
||||
for i := uint64(0); i < 4; i++ {
|
||||
offset := nextReg.offset + i*clsSize
|
||||
reg := ®ion{
|
||||
parent: nextReg,
|
||||
class: clsType,
|
||||
offset: offset,
|
||||
}
|
||||
cp.free[offset] = reg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pa *PoolAllocator) merge(parent *region) error {
|
||||
// nothing to merge
|
||||
if parent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
childCls := parent.class - 1
|
||||
childPool := pa.pools[childCls]
|
||||
// no child nodes to merge, try to merge parent
|
||||
if childPool == nil {
|
||||
return pa.merge(parent.parent)
|
||||
}
|
||||
|
||||
childSize, err := GetMemoryClassSize(childCls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if all the child nodes are free
|
||||
var children []*region
|
||||
for i := uint64(0); i < 4; i++ {
|
||||
child, free := childPool.free[parent.offset+i*childSize]
|
||||
if !free {
|
||||
return ErrEarlyMerge
|
||||
}
|
||||
children = append(children, child)
|
||||
}
|
||||
|
||||
// at this point all the child nodes will be free and we can merge
|
||||
for _, child := range children {
|
||||
delete(childPool.free, child.offset)
|
||||
}
|
||||
|
||||
if err := pa.markFree(parent.class, parent.offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pa.merge(parent.parent)
|
||||
}
|
||||
|
||||
// markFree internally moves a region with `offset` from busy to free map
|
||||
func (pa *PoolAllocator) markFree(memCls classType, offset uint64) error {
|
||||
clsPool := pa.pools[memCls]
|
||||
if clsPool == nil {
|
||||
return ErrEmptyPoolOperation
|
||||
}
|
||||
|
||||
if reg, exists := clsPool.busy[offset]; exists {
|
||||
clsPool.free[offset] = reg
|
||||
delete(clsPool.busy, offset)
|
||||
return nil
|
||||
}
|
||||
return ErrNotAllocated
|
||||
}
|
||||
|
||||
// markBusy internally moves a region with `offset` from free to busy map
|
||||
func (pa *PoolAllocator) markBusy(memCls classType, offset uint64) error {
|
||||
clsPool := pa.pools[memCls]
|
||||
if clsPool == nil {
|
||||
return ErrEmptyPoolOperation
|
||||
}
|
||||
|
||||
if reg, exists := clsPool.free[offset]; exists {
|
||||
clsPool.busy[offset] = reg
|
||||
delete(clsPool.free, offset)
|
||||
return nil
|
||||
}
|
||||
return ErrNotAllocated
|
||||
}
|
28
vendor/github.com/Microsoft/hcsshim/internal/memory/types.go
generated
vendored
Normal file
28
vendor/github.com/Microsoft/hcsshim/internal/memory/types.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package memory
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
type classType uint32
|
||||
|
||||
const (
|
||||
MiB = 1024 * 1024
|
||||
GiB = 1024 * MiB
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotEnoughSpace = errors.New("not enough space")
|
||||
ErrNotAllocated = errors.New("no memory allocated at the given offset")
|
||||
)
|
||||
|
||||
// MappedRegion represents a memory block with an offset
|
||||
type MappedRegion interface {
|
||||
Offset() uint64
|
||||
Size() uint64
|
||||
Type() classType
|
||||
}
|
||||
|
||||
// Allocator is an interface for memory allocation
|
||||
type Allocator interface {
|
||||
Allocate(uint64) (MappedRegion, error)
|
||||
Release(MappedRegion) error
|
||||
}
|
Reference in New Issue
Block a user