@@ -86,6 +86,16 @@ import (
86
86
// allow easy access everywhere.
87
87
var IOUringEnabled = false
88
88
89
+ // SaveRestoreBinMode is the mode for the save/restore binary.
90
+ type SaveRestoreBinMode string
91
+
92
+ const (
93
+ // SaveRestoreBinSave is the save mode for the save/restore binary.
94
+ SaveRestoreBinSave SaveRestoreBinMode = "save"
95
+ // SaveRestoreBinRestore is the restore mode for the save/restore binary.
96
+ SaveRestoreBinRestore SaveRestoreBinMode = "restore"
97
+ )
98
+
89
99
// UserCounters is a set of user counters.
90
100
//
91
101
// +stateify savable
@@ -370,6 +380,16 @@ type Kernel struct {
370
380
371
381
// UnixSocketOpts stores configuration options for management of unix sockets.
372
382
UnixSocketOpts transport.UnixSocketOpts
383
+
384
+ // SaveRestoreBinPath is the path to the save/restore binary. It is executed
385
+ // with the argument "save" before the kernel is saved and "restore" after
386
+ // the kernel is restored and restarted.
387
+ SaveRestoreBinPath string
388
+
389
+ // SaveRestoreBinTimeout is the timeout for the save/restore binary. If the
390
+ // binary fails to exit within this timeout the save/restore operation will
391
+ // fail.
392
+ SaveRestoreBinTimeout time.Duration
373
393
}
374
394
375
395
// InitKernelArgs holds arguments to Init.
@@ -2072,3 +2092,85 @@ func (k *Kernel) ContainerName(cid string) string {
2072
2092
defer k .extMu .Unlock ()
2073
2093
return k .containerNames [cid ]
2074
2094
}
2095
+
2096
+ // ExecSaveRestoreBin creates a new process that executes the save/restore
2097
+ // binary. If the kernel has been started, the process is immediately started
2098
+ // and the method waits for it to exit. Otherwise, the caller is responsible
2099
+ // for starting and waiting for the process.
2100
+ func (k * Kernel ) ExecSaveRestoreBin (mode SaveRestoreBinMode ) (* ThreadGroup , error ) {
2101
+ if k .SaveRestoreBinPath == "" {
2102
+ return nil , nil
2103
+ }
2104
+ sctx := k .SupervisorContext ()
2105
+ leader := k .GlobalInit ().Leader ()
2106
+ contID := leader .ContainerID ()
2107
+ mntns := leader .MountNamespace ()
2108
+ if mntns == nil || ! mntns .TryIncRef () {
2109
+ log .Warningf ("PID %d in container %q has exited, skipping CUDA checkpoint for it" , leader .ThreadGroup ().ID (), contID )
2110
+ return nil , nil
2111
+ }
2112
+ fdTable := leader .FDTable ()
2113
+ fdTable .IncRef ()
2114
+ root := mntns .Root (sctx )
2115
+ cu := cleanup .Make (func () {
2116
+ root .DecRef (sctx )
2117
+ })
2118
+ defer cu .Clean ()
2119
+ ctx := vfs .WithRoot (sctx , root )
2120
+ cu .Add (func () {
2121
+ mntns .DecRef (ctx )
2122
+ })
2123
+
2124
+ argv := []string {k .SaveRestoreBinPath , string (mode )}
2125
+ leader .FDTable ().IncRef ()
2126
+ cu .Add (func () {
2127
+ fdTable .DecRef (ctx )
2128
+ })
2129
+ defer leader .FDTable ().DecRef (ctx )
2130
+
2131
+ mntns .IncRef ()
2132
+ args := CreateProcessArgs {
2133
+ Filename : argv [0 ],
2134
+ Argv : argv ,
2135
+ ContainerID : contID ,
2136
+ MountNamespace : mntns ,
2137
+ PIDNamespace : k .RootPIDNamespace (),
2138
+ UTSNamespace : k .RootUTSNamespace (),
2139
+ IPCNamespace : k .RootIPCNamespace (),
2140
+ Credentials : leader .Credentials (),
2141
+ Umask : 0022 ,
2142
+ Limits : limits .NewLimitSet (),
2143
+ FDTable : fdTable ,
2144
+ Origin : OriginExec ,
2145
+ }
2146
+ tg , _ , err := k .CreateProcess (args )
2147
+ if err != nil {
2148
+ return nil , fmt .Errorf ("failed to create process: %w" , err )
2149
+ }
2150
+ if k .started {
2151
+ k .StartProcess (tg )
2152
+ return nil , k .WaitForSaveRestoreBin (tg )
2153
+ }
2154
+ return tg , nil
2155
+ }
2156
+
2157
+ // WaitForSaveRestoreBin waits for the save/restore binary to exit. If the
2158
+ // SaveRestoreBinTimeout is exceeded, the save/restore binary is killed and
2159
+ // the method returns an error.
2160
+ func (k * Kernel ) WaitForSaveRestoreBin (saveRestoreTg * ThreadGroup ) error {
2161
+ waitC := make (chan struct {})
2162
+ go func () {
2163
+ saveRestoreTg .WaitExited ()
2164
+ waitC <- struct {}{}
2165
+ }()
2166
+ select {
2167
+ case <- waitC :
2168
+ if saveRestoreTg .ExitStatus () != 0 {
2169
+ return fmt .Errorf ("%v exited with non-zero status %d" , k .SaveRestoreBinPath , saveRestoreTg .ExitStatus ())
2170
+ }
2171
+ case <- time .After (k .SaveRestoreBinTimeout ):
2172
+ saveRestoreTg .SendSignal (& linux.SignalInfo {Signo : int32 (linux .SIGKILL )})
2173
+ return fmt .Errorf ("%s timed out after %v" , k .SaveRestoreBinPath , k .SaveRestoreBinTimeout )
2174
+ }
2175
+ return nil
2176
+ }
0 commit comments