diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go index 964730384..14ea29934 100644 --- a/pkg/sentry/kernel/kernel.go +++ b/pkg/sentry/kernel/kernel.go @@ -624,7 +624,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, pagesMetadata, pagesFile *fd.FD, mfOpts pgalloc.SaveOpts) error { +func (k *Kernel) SaveTo(ctx context.Context, w, pagesMetadata io.Writer, pagesFile *fd.FD, mfOpts pgalloc.SaveOpts) error { saveStart := time.Now() // Do not allow other Kernel methods to affect it while it's being saved. @@ -739,7 +739,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, pagesMetadata, pagesFile *fd.FD, timeReady chan struct{}, net inet.Stack, clocks sentrytime.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error { +func (k *Kernel) LoadFrom(ctx context.Context, r, pagesMetadata io.Reader, pagesFile *fd.FD, timeReady chan struct{}, net inet.Stack, clocks sentrytime.Clocks, vfsOpts *vfs.CompleteRestoreOptions) error { loadStart := time.Now() var ( @@ -837,7 +837,7 @@ func (k *Kernel) LoadFrom(ctx context.Context, r io.Reader, pagesMetadata, pages return nil } -func (k *Kernel) loadMemoryFiles(ctx context.Context, r io.Reader, pagesMetadata, pagesFile *fd.FD) error { +func (k *Kernel) loadMemoryFiles(ctx context.Context, r, pagesMetadata io.Reader, pagesFile *fd.FD) error { // Load the memory files' state. memoryStart := time.Now() pmr := r diff --git a/pkg/sentry/state/state.go b/pkg/sentry/state/state.go index 65fc8d1ae..45ec19905 100644 --- a/pkg/sentry/state/state.go +++ b/pkg/sentry/state/state.go @@ -16,6 +16,7 @@ package state import ( + "bufio" "fmt" "io" @@ -98,8 +99,17 @@ func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Wat if err != nil { err = ErrStateFile{err} } else { + var pagesMetadata io.Writer + if opts.PagesMetadata != nil { + // //pkg/state/wire writes one byte at a time; buffer these writes + // to avoid making one syscall per write. For the "main" state + // file, this buffering is handled by statefile.NewWriter() => + // compressio.Writer or compressio.NewSimpleWriter(). + pagesMetadata = bufio.NewWriter(opts.PagesMetadata) + } + // Save the kernel. - err = k.SaveTo(ctx, wc, opts.PagesMetadata, opts.PagesFile, opts.MemoryFileSaveOpts) + err = k.SaveTo(ctx, wc, pagesMetadata, opts.PagesFile, opts.MemoryFileSaveOpts) // ENOSPC is a state file error. This error can only come from // writing the state file, and not from fs.FileOperations.Fsync @@ -111,6 +121,11 @@ func (opts SaveOpts) Save(ctx context.Context, k *kernel.Kernel, w *watchdog.Wat if closeErr := wc.Close(); err == nil && closeErr != nil { err = ErrStateFile{closeErr} } + if pagesMetadata != nil { + if flushErr := pagesMetadata.(*bufio.Writer).Flush(); err == nil && flushErr != nil { + err = ErrStateFile{flushErr} + } + } } opts.Callback(err) return err @@ -140,9 +155,17 @@ func (opts LoadOpts) Load(ctx context.Context, k *kernel.Kernel, timeReady chan if err != nil { return ErrStateFile{err} } + var pagesMetadata io.Reader + if opts.PagesMetadata != nil { + // //pkg/state/wire reads one byte at a time; buffer these reads to + // avoid making one syscall per read. For the "main" state file, this + // buffering is handled by statefile.NewReader() => compressio.Reader + // or compressio.NewSimpleReader(). + pagesMetadata = bufio.NewReader(opts.PagesMetadata) + } previousMetadata = m // Restore the Kernel object graph. - return k.LoadFrom(ctx, r, opts.PagesMetadata, opts.PagesFile, timeReady, n, clocks, vfsOpts) + return k.LoadFrom(ctx, r, pagesMetadata, opts.PagesFile, timeReady, n, clocks, vfsOpts) }