Monorepo for Tangled
tangled.org
1//go:build linux
2
3package microvm
4
5import (
6 "context"
7 "fmt"
8 "time"
9
10 "tangled.org/core/spindle/config"
11 "tangled.org/core/spindle/engine"
12 "tangled.org/core/spindle/models"
13)
14
15// memory buffer for qemu process / slirp4netns itself
16const runnerBufferMiB = 96
17
18type Resources struct {
19 MemoryMiB int64
20 VCPUs int64
21 DiskMiB int64
22}
23
24func (r Resources) Fits(limit Resources) bool {
25 if limit.MemoryMiB > 0 && r.MemoryMiB > limit.MemoryMiB {
26 return false
27 }
28 if limit.VCPUs > 0 && r.VCPUs > limit.VCPUs {
29 return false
30 }
31 if limit.DiskMiB > 0 && r.DiskMiB > limit.DiskMiB {
32 return false
33 }
34 return true
35}
36
37func (r Resources) Add(other Resources) Resources {
38 return Resources{
39 MemoryMiB: r.MemoryMiB + other.MemoryMiB,
40 VCPUs: r.VCPUs + other.VCPUs,
41 DiskMiB: r.DiskMiB + other.DiskMiB,
42 }
43}
44
45func (r Resources) Sub(other Resources) Resources {
46 return Resources{
47 MemoryMiB: max(0, r.MemoryMiB-other.MemoryMiB),
48 VCPUs: max(0, r.VCPUs-other.VCPUs),
49 DiskMiB: max(0, r.DiskMiB-other.DiskMiB),
50 }
51}
52
53func (r Resources) String() string {
54 return fmt.Sprintf("memory=%dMiB vcpus=%d disk=%dMiB", r.MemoryMiB, r.VCPUs, r.DiskMiB)
55}
56
57func newVMBudgetConfig(cfg config.MicroVMPipelines) (Resources, Resources, time.Duration) {
58 budget := Resources{
59 MemoryMiB: cfg.MaxTotalMemoryMiB,
60 VCPUs: cfg.MaxTotalVCPUs,
61 DiskMiB: cfg.MaxTotalDiskMiB,
62 }
63 maxReq := Resources{
64 MemoryMiB: cfg.MaxWorkflowMemoryMiB,
65 VCPUs: cfg.MaxWorkflowVCPUs,
66 DiskMiB: cfg.MaxWorkflowDiskMiB,
67 }
68 return budget, maxReq, cfg.AgingThreshold
69}
70
71func (e *Engine) AcquireWorkflowSlot(ctx context.Context, wid models.WorkflowId, wf *models.Workflow) (engine.WorkflowSlot, error) {
72 state, ok := wf.Data.(*workflowState)
73 if !ok || state == nil {
74 return nil, fmt.Errorf("microVM workflow state is not initialized")
75 }
76 if e.scheduler == nil {
77 return engine.NoopSlot{}, nil
78 }
79 req := resourcesForImage(state.ImageSpec)
80 if req.MemoryMiB < 0 || req.VCPUs < 0 || req.DiskMiB < 0 {
81 return nil, fmt.Errorf("microVM resource request must not be negative: %s", req)
82 }
83 return e.scheduler.Acquire(ctx, req)
84}
85
86func resourcesForImage(spec ImageSpec) Resources {
87 var diskMiB int64
88 for _, volume := range spec.Volumes {
89 diskMiB += volume.SizeMiB
90 }
91 return Resources{
92 MemoryMiB: int64(spec.MemoryMiB) + runnerBufferMiB,
93 VCPUs: int64(spec.VCPUs),
94 DiskMiB: diskMiB,
95 }
96}