mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user