Create separate MemoryFile metadata file in compression=none mode.

This helps in parallelizing MemoryFile restore with kernel restore.

PiperOrigin-RevId: 629378902
This commit is contained in:
Ayush Ranjan
2024-04-30 05:21:53 -07:00
committed by gVisor bot
parent 5c5df9d78c
commit a3ae7f25a0
6 changed files with 83 additions and 30 deletions
+21 -6
View File
@@ -44,12 +44,14 @@ type SaveOpts struct {
// Metadata is the set of metadata to prepend to the state file.
Metadata map[string]string `json:"metadata"`
// HavePagesFile indicates whether the checkpoint pages file is provided.
// HavePagesFile indicates whether the pages file and its corresponding
// metadata file is provided.
HavePagesFile bool `json:"have_pages_file"`
// FilePayload contains the following:
// 1. checkpoint state file.
// 2. optional checkpoint pages file.
// 2. optional checkpoint pages metadata file.
// 3. optional checkpoint pages file.
urpc.FilePayload
// Resume indicates if the sandbox process should continue running
@@ -61,15 +63,20 @@ type SaveOpts struct {
func (s *State) Save(o *SaveOpts, _ *struct{}) error {
wantFiles := 1
if o.HavePagesFile {
wantFiles++
wantFiles += 2
}
if gotFiles := len(o.FilePayload.Files); gotFiles != wantFiles {
return fmt.Errorf("got %d files, wanted %d", gotFiles, wantFiles)
}
// Save to the first provided stream.
stateFile, err := o.ReleaseFD(0)
if err != nil {
return err
}
defer stateFile.Close()
saveOpts := state.SaveOpts{
Destination: o.FilePayload.Files[0],
Destination: stateFile,
Key: o.Key,
Metadata: o.Metadata,
Callback: func(err error) {
@@ -85,9 +92,17 @@ func (s *State) Save(o *SaveOpts, _ *struct{}) error {
}
},
}
defer o.FilePayload.Files[0].Close()
if o.HavePagesFile {
saveOpts.PagesFile = o.FilePayload.Files[1]
saveOpts.PagesMetadata, err = o.ReleaseFD(1)
if err != nil {
return err
}
defer saveOpts.PagesMetadata.Close()
saveOpts.PagesFile, err = o.ReleaseFD(2)
if err != nil {
return err
}
defer saveOpts.PagesFile.Close()
}
return saveOpts.Save(s.Kernel.SupervisorContext(), s.Kernel, s.Watchdog)
+15 -8
View File
@@ -35,7 +35,6 @@ import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"time"
@@ -581,7 +580,7 @@ func loadPrivateMFs(ctx context.Context, r io.Reader, pr *statefile.AsyncReader)
// SaveTo saves the state of k to w.
//
// Preconditions: The kernel must be paused throughout the call to SaveTo.
func (k *Kernel) SaveTo(ctx context.Context, w io.Writer, pagesFile *os.File) error {
func (k *Kernel) SaveTo(ctx context.Context, w io.Writer, pagesMetadata, pagesFile *fd.FD) error {
saveStart := time.Now()
// Do not allow other Kernel methods to affect it while it's being saved.
@@ -649,14 +648,18 @@ func (k *Kernel) SaveTo(ctx context.Context, w io.Writer, pagesFile *os.File) er
// Save the memory files' state.
memoryStart := time.Now()
pw := io.Writer(w)
pmw := w
if pagesMetadata != nil {
pmw = pagesMetadata
}
pw := w
if pagesFile != nil {
pw = pagesFile
}
if err := k.mf.SaveTo(ctx, w, pw); err != nil {
if err := k.mf.SaveTo(ctx, pmw, pw); err != nil {
return err
}
if err := savePrivateMFs(ctx, w, pw, mfsToSave); err != nil {
if err := savePrivateMFs(ctx, pmw, pw, mfsToSave); err != nil {
return err
}
log.Infof("Memory files save took [%s].", time.Since(memoryStart))
@@ -692,7 +695,7 @@ func (k *Kernel) invalidateUnsavableMappings(ctx context.Context) error {
}
// LoadFrom returns a new Kernel loaded from args.
func (k *Kernel) LoadFrom(ctx context.Context, r io.Reader, pagesFile *fd.FD, timeReady chan struct{}, net inet.Stack, clocks sentrytime.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error {
func (k *Kernel) LoadFrom(ctx context.Context, r io.Reader, pagesMetadata, pagesFile *fd.FD, timeReady chan struct{}, net inet.Stack, clocks sentrytime.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error {
loadStart := time.Now()
k.runningTasksCond.L = &k.runningTasksMu
@@ -734,14 +737,18 @@ func (k *Kernel) LoadFrom(ctx context.Context, r io.Reader, pagesFile *fd.FD, ti
// Load the memory files' state.
memoryStart := time.Now()
pmr := r
if pagesMetadata != nil {
pmr = pagesMetadata
}
var pr *statefile.AsyncReader
if pagesFile != nil {
pr = statefile.NewAsyncReader(pagesFile, 0 /* off */)
}
if err := k.mf.LoadFrom(ctx, r, pr); err != nil {
if err := k.mf.LoadFrom(ctx, pmr, pr); err != nil {
return err
}
if err := loadPrivateMFs(ctx, r, pr); err != nil {
if err := loadPrivateMFs(ctx, pmr, pr); err != nil {
return err
}
if pr != nil {
+14 -7
View File
@@ -18,7 +18,6 @@ package state
import (
"fmt"
"io"
"os"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
@@ -50,9 +49,13 @@ type SaveOpts struct {
// Destination is the save target.
Destination io.Writer
// PagesMetadata is the file into which MemoryFile metadata is stored if
// PagesMetadata is non-nil. Otherwise this content is stored in Destination.
PagesMetadata *fd.FD
// PagesFile is the file in which all MemoryFile pages are stored if
// PagesFile is non-nil.
PagesFile *os.File
// PagesFile is non-nil. Otherwise this content is stored in Destination.
PagesFile *fd.FD
// Key is used for state integrity check.
Key []byte
@@ -92,7 +95,7 @@ func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Wat
err = ErrStateFile{err}
} else {
// Save the kernel.
err = k.SaveTo(ctx, wc, opts.PagesFile)
err = k.SaveTo(ctx, wc, opts.PagesMetadata, opts.PagesFile)
// ENOSPC is a state file error. This error can only come from
// writing the state file, and not from fs.FileOperations.Fsync
@@ -111,11 +114,15 @@ func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Wat
// LoadOpts contains load-related options.
type LoadOpts struct {
// Destination is the load source.
// Source is the load source.
Source io.Reader
// PagesMetadata is the file into which MemoryFile metadata is stored if
// PagesMetadata is non-nil. Otherwise this content is stored in Source.
PagesMetadata *fd.FD
// PagesFile is the file in which all MemoryFile pages are stored if
// PagesFile is non-nil.
// PagesFile is non-nil. Otherwise this content is stored in Source.
PagesFile *fd.FD
// Key is used for state integrity check.
@@ -133,5 +140,5 @@ func (opts LoadOpts) Load(ctx context.Context, k *kernel.Kernel, timeReady chan
previousMetadata = m
// Restore the Kernel object graph.
return k.LoadFrom(ctx, r, opts.PagesFile, timeReady, n, clocks, vfsOpts)
return k.LoadFrom(ctx, r, opts.PagesMetadata, opts.PagesFile, timeReady, n, clocks, vfsOpts)
}
+12 -4
View File
@@ -453,8 +453,9 @@ func (cm *containerManager) PortForward(opts *PortForwardOpts, _ *struct{}) erro
type RestoreOpts struct {
// FilePayload contains the state file to be restored, followed in order by:
// 1. checkpoint state file.
// 2. optional checkpoint pages file.
// 3. optional platform device file.
// 2. optional checkpoint pages metadata file.
// 3. optional checkpoint pages file.
// 4. optional platform device file.
urpc.FilePayload
HavePagesFile bool
HaveDeviceFile bool
@@ -496,12 +497,19 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error {
fileIdx := 1
if o.HavePagesFile {
pagesFile, err := o.ReleaseFD(fileIdx)
cm.restorer.pagesMetadata, err = o.ReleaseFD(fileIdx)
if err != nil {
return err
}
defer cm.restorer.pagesMetadata.Close()
fileIdx++
cm.restorer.pagesFile, err = o.ReleaseFD(fileIdx)
if err != nil {
return err
}
defer cm.restorer.pagesFile.Close()
fileIdx++
cm.restorer.pagesFile = pagesFile
}
if o.HaveDeviceFile {
+7 -3
View File
@@ -48,6 +48,9 @@ const (
// CheckpointStateFileName is the file within the given image-path's
// directory which contains the container's saved state.
CheckpointStateFileName = "checkpoint.img"
// CheckpointPagesMetadataFileName is the file within the given image-path's
// directory containing the container's MemoryFile metadata.
CheckpointPagesMetadataFileName = "pages_meta.img"
// CheckpointPagesFileName is the file within the given image-path's
// directory containing the container's MemoryFile pages.
CheckpointPagesFileName = "pages.img"
@@ -68,8 +71,9 @@ type restorer struct {
containers []*containerInfo
// Files used by restore to rehydrate the state.
stateFile io.ReadCloser
pagesFile *fd.FD
stateFile io.ReadCloser
pagesMetadata *fd.FD
pagesFile *fd.FD
// deviceFile is the required to start the platform.
deviceFile *fd.FD
@@ -224,7 +228,7 @@ func (r *restorer) restore(l *Loader) error {
ctx = context.WithValue(ctx, pgalloc.CtxMemoryFileMap, mfmap)
// Load the state.
loadOpts := state.LoadOpts{Source: r.stateFile, PagesFile: r.pagesFile}
loadOpts := state.LoadOpts{Source: r.stateFile, PagesMetadata: r.pagesMetadata, PagesFile: r.pagesFile}
if err := loadOpts.Load(ctx, l.k, nil, netns.Stack(), time.NewCalibratedClocks(), &vfs.CompleteRestoreOptions{}); err != nil {
return err
}
+14 -2
View File
@@ -468,8 +468,14 @@ func (s *Sandbox) Restore(conf *config.Config, cid string, imagePath string, dir
}
if pf, err := os.OpenFile(pagesFileName, pagesReadFlags, 0); err == nil {
defer pf.Close()
pagesMetadataFileName := path.Join(imagePath, boot.CheckpointPagesMetadataFileName)
pmf, err := os.Open(pagesMetadataFileName)
if err != nil {
return fmt.Errorf("opening restore image file %q failed: %v", pagesMetadataFileName, err)
}
defer pmf.Close()
opt.HavePagesFile = true
opt.FilePayload.Files = append(opt.FilePayload.Files, pf)
opt.FilePayload.Files = append(opt.FilePayload.Files, pmf, pf)
} else if !os.IsNotExist(err) {
return fmt.Errorf("opening restore image file %q failed: %v", pagesFileName, err)
}
@@ -1341,7 +1347,13 @@ func (s *Sandbox) Checkpoint(cid string, imagePath string, options statefile.Opt
return fmt.Errorf("creating checkpoint pages file %q: %w", pagesFilePath, err)
}
defer pf.Close()
opt.FilePayload.Files = append(opt.FilePayload.Files, pf)
pagesMetadataFilePath := filepath.Join(imagePath, boot.CheckpointPagesMetadataFileName)
pmf, err := os.OpenFile(pagesMetadataFilePath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0644)
if err != nil {
return fmt.Errorf("creating checkpoint pages metadata file %q: %w", pagesMetadataFilePath, err)
}
defer pmf.Close()
opt.FilePayload.Files = append(opt.FilePayload.Files, pmf, pf)
opt.HavePagesFile = true
}