gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/boot/autosave.go (about)

     1  // Copyright 2024 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package boot
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"os"
    21  
    22  	"gvisor.dev/gvisor/pkg/abi/linux"
    23  	"gvisor.dev/gvisor/pkg/log"
    24  	"gvisor.dev/gvisor/pkg/sentry/arch"
    25  	"gvisor.dev/gvisor/pkg/sentry/kernel"
    26  	"gvisor.dev/gvisor/pkg/sentry/state"
    27  	"gvisor.dev/gvisor/pkg/sentry/strace"
    28  	"gvisor.dev/gvisor/pkg/sync"
    29  )
    30  
    31  func getSaveOpts(l *Loader, k *kernel.Kernel, isResume bool) state.SaveOpts {
    32  	t, _ := state.CPUTime()
    33  	log.Infof("Before save CPU usage: %s", t.String())
    34  	saveOpts := state.SaveOpts{
    35  		Key:    nil,
    36  		Resume: isResume,
    37  		Callback: func(err error) {
    38  			t1, _ := state.CPUTime()
    39  			log.Infof("Save CPU usage: %s", (t1 - t).String())
    40  			if err == nil {
    41  				log.Infof("Save succeeded: exiting...")
    42  				k.SetSaveSuccess(true)
    43  			} else {
    44  				log.Warningf("Save failed: exiting... %v", err)
    45  				k.SetSaveError(err)
    46  			}
    47  
    48  			if !isResume {
    49  				// Kill the sandbox.
    50  				k.Kill(linux.WaitStatusExit(0))
    51  			}
    52  		},
    53  	}
    54  	return saveOpts
    55  }
    56  
    57  func getTargetForSaveResume(l *Loader) func(k *kernel.Kernel) {
    58  	return func(k *kernel.Kernel) {
    59  		saveOpts := getSaveOpts(l, k, true /* isResume */)
    60  		// Store the state file contents in a buffer for save-resume.
    61  		// There is no need to verify the state file, we just need the
    62  		// sandbox to continue running after save.
    63  		var buf bytes.Buffer
    64  		saveOpts.Destination = &buf
    65  		saveOpts.Save(k.SupervisorContext(), k, l.watchdog)
    66  	}
    67  }
    68  
    69  func getTargetForSaveRestore(l *Loader, f *os.File) func(k *kernel.Kernel) {
    70  	var once sync.Once
    71  	return func(k *kernel.Kernel) {
    72  		once.Do(func() {
    73  			saveOpts := getSaveOpts(l, k, false /* isResume */)
    74  			saveOpts.Destination = f
    75  			saveOpts.Save(k.SupervisorContext(), k, l.watchdog)
    76  		})
    77  	}
    78  }
    79  
    80  // EnableAutosave enables auto save restore in syscall tests.
    81  func EnableAutosave(l *Loader, f *os.File, isResume bool) error {
    82  	var target func(k *kernel.Kernel)
    83  	if isResume {
    84  		target = getTargetForSaveResume(l)
    85  	} else {
    86  		target = getTargetForSaveRestore(l, f)
    87  	}
    88  
    89  	for _, table := range kernel.SyscallTables() {
    90  		sys, ok := strace.Lookup(table.OS, table.Arch)
    91  		if !ok {
    92  			continue
    93  		}
    94  		if err := configureInitSyscall(table, sys, "init_module", kernel.ExternalAfterEnable); err != nil {
    95  			return err
    96  		}
    97  		// Set external args to our closure above.
    98  		table.External = target
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  // configureInitSyscall sets the trigger for the S/R syscall tests and the callback
   105  // method to be called after the sycall is executed.
   106  func configureInitSyscall(table *kernel.SyscallTable, sys strace.SyscallMap, initSyscall string, syscallFlag uint32) error {
   107  	sl := make(map[uintptr]bool)
   108  	sysno, ok := sys.ConvertToSysno(initSyscall)
   109  	if !ok {
   110  		return fmt.Errorf("syscall %q not found", initSyscall)
   111  	}
   112  	sl[sysno] = true
   113  	log.Infof("sysno %v name %v", sysno, initSyscall)
   114  	table.FeatureEnable.Enable(syscallFlag, sl, false)
   115  	table.ExternalFilterBefore = func(*kernel.Task, uintptr, arch.SyscallArguments) bool {
   116  		return false
   117  	}
   118  	// Sets ExternalFilterAfter to true which calls the closure assigned to
   119  	// External after the syscall is executed.
   120  	table.ExternalFilterAfter = func(*kernel.Task, uintptr, arch.SyscallArguments) bool {
   121  		return true
   122  	}
   123  	return nil
   124  }