Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- uses: actions/setup-node@v6
with:
node-version: '20'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
fail-fast: false
matrix:
module: [misc, networking, snapshotting]
module: [misc, networking, snapshotting, memory/manager]
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v7
Expand Down Expand Up @@ -64,8 +64,8 @@ jobs:
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
run: |
make -C $MODULE test
make -C $MODULE test-man
make -C "$MODULE" test
make -C "$MODULE" test-man

profile-unit-test:
if: false
Expand Down
4 changes: 2 additions & 2 deletions bin/containerd-shim-aws-firecracker
Git LFS file not shown
4 changes: 2 additions & 2 deletions bin/firecracker
Git LFS file not shown
4 changes: 2 additions & 2 deletions bin/firecracker-containerd
Git LFS file not shown
4 changes: 2 additions & 2 deletions bin/firecracker-ctr
Git LFS file not shown
4 changes: 2 additions & 2 deletions bin/jailer
Git LFS file not shown
21 changes: 5 additions & 16 deletions ctriface/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@
EXTRAGOARGS:=-v -race -cover
EXTRATESTFILES:=iface_test.go iface.go orch_options.go orch.go
BENCHFILES:=bench_test.go iface.go orch_options.go orch.go
# User-level page faults are temporarily disabled (gh-807)
# WITHUPF:=-upf
# WITHLAZY:=-lazy
WITHUPF:=
WITHLAZY:=
UPFARGS:=-upf -lazy
STARGZ:=-ss 'proxy' -img 'ghcr.io/vhive-serverless/helloworld:var_workload-esgz'
DOCKER_CREDENTIALS:=-dockerCredentials '{"docker-credentials":{"ghcr.io":{"username":"","password":""}}}'
GOBENCH:=-v -timeout 1500s
Expand All @@ -39,7 +35,7 @@ test:
sudo env "PATH=$(PATH)" go test $(EXTRATESTFILES) $(EXTRAGOARGS)
./../scripts/clean_fcctr.sh
sudo mkdir -m777 -p $(CTRDLOGDIR) && sudo env "PATH=$(PATH)" /usr/local/bin/firecracker-containerd --config /etc/firecracker-containerd/config.toml 1>$(CTRDLOGDIR)/ctriface_log.out 2>$(CTRDLOGDIR)/ctriface_log.err &
sudo env "PATH=$(PATH)" go test $(EXTRATESTFILES) $(EXTRAGOARGS) -args $(WITHUPF)
sudo env "PATH=$(PATH)" go test $(EXTRATESTFILES) $(EXTRAGOARGS) -args $(UPFARGS)
./../scripts/clean_fcctr.sh

test-man:
Expand All @@ -48,10 +44,7 @@ test-man:
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestSnapLoad
./../scripts/clean_fcctr.sh
sudo mkdir -m777 -p $(CTRDLOGDIR) && sudo env "PATH=$(PATH)" /usr/local/bin/firecracker-containerd --config /etc/firecracker-containerd/config.toml 1>$(CTRDLOGDIR)/ctriface_log_upf_man_travis.out 2>$(CTRDLOGDIR)/ctriface_log_upf_man_travis.err &
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestSnapLoad -args $(WITHUPF)
./../scripts/clean_fcctr.sh
sudo mkdir -m777 -p $(CTRDLOGDIR) && sudo env "PATH=$(PATH)" /usr/local/bin/firecracker-containerd --config /etc/firecracker-containerd/config.toml 1>$(CTRDLOGDIR)/ctriface_log_lazy_man_travis.out 2>$(CTRDLOGDIR)/ctriface_log_lazy_man_travis.err &
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestSnapLoad -args $(WITHUPF) $(WITHLAZY)
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestSnapLoad -args $(UPFARGS)
./../scripts/clean_fcctr.sh
sudo mkdir -m777 -p $(CTRDLOGDIR) && sudo env "PATH=$(PATH)" /usr/local/bin/firecracker-containerd --config /etc/firecracker-containerd/config.toml 1>$(CTRDLOGDIR)/ctriface_log_remote_snap_create_man_travis.out 2>$(CTRDLOGDIR)/ctriface_log_remote_snap_create_man_travis.err &
# Creates a remote snapshot.
Expand All @@ -74,12 +67,8 @@ test-skip:
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelPhasedSnapLoad
./../scripts/clean_fcctr.sh
sudo mkdir -m777 -p $(CTRDLOGDIR) && sudo env "PATH=$(PATH)" /usr/local/bin/firecracker-containerd --config /etc/firecracker-containerd/config.toml 1>$(CTRDLOGDIR)/ctriface_log_upf_man_skip.out 2>$(CTRDLOGDIR)/ctriface_log_upf_man_skip.err &
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelSnapLoad -args $(WITHUPF)
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelPhasedSnapLoad -args $(WITHUPF)
./../scripts/clean_fcctr.sh
sudo mkdir -m777 -p $(CTRDLOGDIR) && sudo env "PATH=$(PATH)" /usr/local/bin/firecracker-containerd --config /etc/firecracker-containerd/config.toml 1>$(CTRDLOGDIR)/ctriface_log_lazy_man_skip.out 2>$(CTRDLOGDIR)/ctriface_log_lazy_man_skip.err &
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelSnapLoad -args $(WITHUPF) $(WITHLAZY)
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelPhasedSnapLoad -args $(WITHUPF) $(WITHLAZY)
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelSnapLoad -args $(UPFARGS)
sudo env "PATH=$(PATH)" go test $(EXTRAGOARGS) -run TestParallelPhasedSnapLoad -args $(UPFARGS)
./../scripts/clean_fcctr.sh


Expand Down
120 changes: 79 additions & 41 deletions ctriface/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,6 @@ func (o *Orchestrator) StartVMWithEnvironment(ctx context.Context, vmID, imageNa
IsLazyMode: o.isLazyMode,
VMMStatePath: o.getSnapshotFile(vmID),
WorkingSetPath: o.getWorkingSetFile(vmID),
// FIXME (gh-807)
//InstanceSockAddr: resp.UPFSockPath,
}
if err := o.memoryManager.RegisterVM(stateCfg); err != nil {
return nil, nil, errors.Wrap(err, "failed to register VM with memory manager")
Expand Down Expand Up @@ -316,6 +314,12 @@ func (o *Orchestrator) StopSingleVM(ctx context.Context, vmID string) error {
logger.WithError(err).Error("failed to stop firecracker-containerd VM")
}

if o.GetUPFEnabled() {
if err := o.memoryManager.Deactivate(vmID); err != nil {
logger.WithError(err).Warn("failed to deactivate VM in memory manager")
}
}

if err := o.vmPool.Free(vmID); err != nil {
logger.Error("failed to free VM from VM pool")
return err
Expand Down Expand Up @@ -359,15 +363,15 @@ func getK8sDNS() []string {
}

func (o *Orchestrator) getVMConfig(vm *misc.VM) *proto.CreateVMRequest {
kernelArgs := "ro noapic reboot=k panic=1 pci=off nomodules systemd.log_color=false systemd.unit=firecracker.target init=/sbin/overlay-init tsc=reliable quiet 8250.nr_uarts=0 ipv6.disable=1"
kernelArgs := "ro noapic reboot=k panic=1 acpi=off pci=off nomodules systemd.log_color=false systemd.journald.forward_to_console systemd.unit=firecracker.target init=/sbin/overlay-init tsc=reliable quiet ipv6.disable=1 console=ttyS0"

return &proto.CreateVMRequest{
VMID: vm.ID,
TimeoutSeconds: 100,
KernelArgs: kernelArgs,
MachineCfg: &proto.FirecrackerMachineConfiguration{
VcpuCount: 1,
MemSizeMib: 256,
MemSizeMib: 512,
},
NetworkInterfaces: []*proto.FirecrackerNetworkInterface{{
AllowMMDS: true,
Expand Down Expand Up @@ -493,13 +497,37 @@ func (o *Orchestrator) CreateSnapshot(ctx context.Context, vmID string, snap *sn
return nil
}

func logSnapshotLoadFailure(logger *log.Entry, snap *snapshotting.Snapshot, conf *proto.CreateVMRequest, err error) {
logger.WithError(err).WithFields(log.Fields{
"snapFilePath": snap.GetSnapshotFilePath(),
"memFilePath": snap.GetMemFilePath(),
"containerSnapshotPath": conf.ContainerSnapshotPath,
"memoryBackendType": conf.GetMemBackend().GetBackendType(),
"memoryBackendPath": conf.GetMemBackend().GetBackendPath(),
}).Error("failed to load snapshot of the VM")
}

func configureSnapshotMemoryBackend(conf *proto.CreateVMRequest, backendType, backendPath string) {
conf.MemFilePath = ""
conf.MemBackend = &proto.MemoryBackend{
BackendType: backendType,
BackendPath: backendPath,
}
}

// LoadSnapshot Loads a snapshot of a VM
func (o *Orchestrator) LoadSnapshot(ctx context.Context, vmID string, snap *snapshotting.Snapshot) (_ *StartVMResponse, _ *metrics.Metric, retErr error) {
if err := o.validateUPFMode(); err != nil {
return nil, nil, err
}

var (
loadSnapshotMetric = metrics.NewMetric()
tStart time.Time
loadErr, activateErr error
loadDone = make(chan int)
loadSnapshotMetric = metrics.NewMetric()
tStart time.Time
loadErr error
activateErr error
deactivateErr error
activateErrChan chan error
)

logger := log.WithFields(log.Fields{"vmID": vmID})
Expand All @@ -524,7 +552,8 @@ func (o *Orchestrator) LoadSnapshot(ctx context.Context, vmID string, snap *snap
conf := o.getVMConfig(vm)
conf.LoadSnapshot = true
conf.SnapshotPath = snap.GetSnapshotFilePath()
conf.MemFilePath = snap.GetMemFilePath()
configureSnapshotMemoryBackend(conf, "File", snap.GetMemFilePath())
uffdSock := filepath.Join(o.getVMBaseDir(vmID), "uffd.sock")

if o.snapshotter == "devmapper" {
if vm.Image, err = o.getImage(ctx, snap.GetImage()); err != nil {
Expand Down Expand Up @@ -583,56 +612,65 @@ func (o *Orchestrator) LoadSnapshot(ctx context.Context, vmID string, snap *snap
}

if o.GetUPFEnabled() {
configureSnapshotMemoryBackend(conf, "Uffd", uffdSock)

if err := o.memoryManager.PrepareSnapshotLoad(manager.SnapshotStateCfg{
VMID: vmID,
VMMStatePath: snap.GetSnapshotFilePath(),
GuestMemPath: snap.GetMemFilePath(),
InstanceSockAddr: uffdSock,
BaseDir: o.getVMBaseDir(vmID),
GuestMemSize: int(conf.MachineCfg.MemSizeMib) * 1024 * 1024,
IsLazyMode: o.isLazyMode,
WorkingSetPath: o.getWorkingSetFile(vmID),
}); err != nil {
return nil, nil, err
}

if err := o.memoryManager.FetchState(vmID); err != nil {
return nil, nil, err
}
}

tStart = time.Now()

go func() {
defer close(loadDone)

if _, loadErr = o.fcClient.CreateVM(ctx, conf); loadErr != nil {
logger.Error("Failed to load snapshot of the VM: ", loadErr)
logger.Errorf("snapFilePath: %s, memFilePath: %s, containerSnapshotPath: %s", snap.GetSnapshotFilePath(), snap.GetMemFilePath(), conf.ContainerSnapshotPath)
files, err := os.ReadDir(filepath.Dir(snap.GetSnapshotFilePath()))
if o.GetUPFEnabled() {
activateErrChan = make(chan error, 1)
socketReadyChan := make(chan struct{}, 1)
go func() {
err := o.memoryManager.Activate(vmID, socketReadyChan)
if err != nil {
logger.Error(err)
}

snapFiles := ""
for _, f := range files {
snapFiles += f.Name() + ", "
logger.WithError(err).Warn("Failed to activate VM in the memory manager")
}
activateErrChan <- err
}()

logger.Error(snapFiles)
select {
case <-socketReadyChan:
case activateErr = <-activateErrChan:
return nil, nil, activateErr
}
}

files, _ = os.ReadDir(filepath.Dir(conf.ContainerSnapshotPath))
if err != nil {
logger.Error(err)
}
if _, loadErr = o.fcClient.CreateVM(ctx, conf); loadErr != nil {
logSnapshotLoadFailure(logger, snap, conf, loadErr)
}

snapFiles = ""
for _, f := range files {
snapFiles += f.Name() + ", "
}
logger.Error(snapFiles)
}
}()
if activateErrChan != nil {
activateErr = <-activateErrChan
}

if o.GetUPFEnabled() {
if activateErr = o.memoryManager.Activate(vmID); activateErr != nil {
logger.Warn("Failed to activate VM in the memory manager", activateErr)
if loadErr != nil && activateErr == nil && activateErrChan != nil {
deactivateErr = o.memoryManager.Deactivate(vmID)
if deactivateErr != nil {
logger.WithError(deactivateErr).Warn("Failed to deactivate VM in the memory manager after snapshot load failure")
}
}

<-loadDone

loadSnapshotMetric.MetricMap[metrics.LoadVMM] = metrics.ToUS(time.Since(tStart))

if loadErr != nil || activateErr != nil {
multierr := multierror.Of(loadErr, activateErr)
if loadErr != nil || activateErr != nil || deactivateErr != nil {
multierr := multierror.Of(loadErr, activateErr, deactivateErr)
return nil, nil, multierr
}

Expand Down
7 changes: 2 additions & 5 deletions ctriface/iface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,6 @@ var (
func TestMain(m *testing.M) {
flag.Parse()

if *isUPFEnabled {
log.Error("User-level page faults are temporarily disabled (gh-807)")
os.Exit(-1)
}

os.Exit(m.Run())
}

Expand All @@ -78,6 +73,8 @@ func TestStartSnapStopLoad(t *testing.T) {
*snapshotter,
"",
WithTestModeOn(true),
WithUPF(*isUPFEnabled),
WithLazyMode(*isLazyMode),
WithDockerCredentials(*dockerCredentials),
)

Expand Down
14 changes: 11 additions & 3 deletions ctriface/orch_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ package ctriface

import (
"encoding/json"
"errors"
"fmt"
)

var errUPFRequiresLazyMode = errors.New("UPF currently requires lazy mode")

// OrchestratorOption Options to pass to Orchestrator
type OrchestratorOption func(*Orchestrator)

Expand Down Expand Up @@ -62,15 +65,20 @@ func WithSnapshotsDir(snapshotsDir string) OrchestratorOption {
}
}

// WithLazyMode Sets the lazy paging mode on (or off),
// where all guest memory pages are brought on demand.
// Only works if snapshots are enabled
// WithLazyMode Sets the lazy paging mode on or off.
func WithLazyMode(isLazyMode bool) OrchestratorOption {
return func(o *Orchestrator) {
o.isLazyMode = isLazyMode
}
}

func (o *Orchestrator) validateUPFMode() error {
if o.isUPFEnabled && !o.isLazyMode {
return errUPFRequiresLazyMode
}
return nil
}

// WithMetricsMode Sets the metrics mode
func WithMetricsMode(isMetricsMode bool) OrchestratorOption {
return func(o *Orchestrator) {
Expand Down
Loading
Loading