checkpoint: check if system supports pre-dumping
Instead of relying on version numbers it is possible to check if CRIU actually supports certain features. This introduces an initial implementation to check if CRIU and the underlying kernel actually support dirty memory tracking for memory pre-dumping. Upstream CRIU also supports the lazy-page migration feature check and additional feature checks can be included in CRIU to reduce the version number parsing. There are also certain CRIU features which depend on one side on the CRIU version but also require certain kernel versions to actually work. CRIU knows if it can do certain things on the kernel it is running on and using the feature check RPC interface makes it easier for runc to decide if the criu+kernel combination will support that feature. Feature checking was introduced with CRIU 1.8. Running with older CRIU versions will ignore the feature check functionality and behave just like it used to. v2: - Do not use reflection to compare requested and responded features. Checking which feature is available is now hardcoded and needs to be adapted for every new feature check. The code is now much more readable and simpler. v3: - Move the variable criuFeat out of the linuxContainer struct, as it is not container specific. Now it is a global variable. Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
parent
f990e58f26
commit
273b7853c8
|
@ -535,6 +535,56 @@ func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struc
|
|||
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
|
||||
}
|
||||
|
||||
var criuFeatures *criurpc.CriuFeatures
|
||||
|
||||
func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.CriuOpts, criuFeat *criurpc.CriuFeatures) error {
|
||||
|
||||
var t criurpc.CriuReqType
|
||||
t = criurpc.CriuReqType_FEATURE_CHECK
|
||||
|
||||
if err := c.checkCriuVersion("1.8"); err != nil {
|
||||
// Feature checking was introduced with CRIU 1.8.
|
||||
// Ignore the feature check if an older CRIU version is used
|
||||
// and just act as before.
|
||||
// As all automated PR testing is done using CRIU 1.7 this
|
||||
// code will not be tested by automated PR testing.
|
||||
return nil
|
||||
}
|
||||
|
||||
// make sure the features we are looking for are really not from
|
||||
// some previous check
|
||||
criuFeatures = nil
|
||||
|
||||
req := &criurpc.CriuReq{
|
||||
Type: &t,
|
||||
// Theoretically this should not be necessary but CRIU
|
||||
// segfaults if Opts is empty.
|
||||
// Fixed in CRIU 2.12
|
||||
Opts: rpcOpts,
|
||||
Features: criuFeat,
|
||||
}
|
||||
|
||||
err := c.criuSwrk(nil, req, criuOpts, false)
|
||||
if err != nil {
|
||||
logrus.Debugf("%s", err)
|
||||
return fmt.Errorf("CRIU feature check failed")
|
||||
}
|
||||
|
||||
logrus.Debugf("Feature check says: %s", criuFeatures)
|
||||
missingFeatures := false
|
||||
|
||||
if *criuFeat.MemTrack && !*criuFeatures.MemTrack {
|
||||
missingFeatures = true
|
||||
logrus.Debugf("CRIU does not support MemTrack")
|
||||
}
|
||||
|
||||
if missingFeatures {
|
||||
return fmt.Errorf("CRIU is missing features")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkCriuVersion checks Criu version greater than or equal to minVersion
|
||||
func (c *linuxContainer) checkCriuVersion(minVersion string) error {
|
||||
var x, y, z, versionReq int
|
||||
|
@ -717,6 +767,14 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
|
||||
var t criurpc.CriuReqType
|
||||
if criuOpts.PreDump {
|
||||
feat := criurpc.CriuFeatures{
|
||||
MemTrack: proto.Bool(true),
|
||||
}
|
||||
|
||||
if err := c.checkCriuFeatures(criuOpts, &rpcOpts, &feat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t = criurpc.CriuReqType_PRE_DUMP
|
||||
} else {
|
||||
t = criurpc.CriuReqType_DUMP
|
||||
|
@ -1018,16 +1076,21 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
|
|||
}
|
||||
|
||||
logrus.Debugf("Using CRIU in %s mode", req.GetType().String())
|
||||
val := reflect.ValueOf(req.GetOpts())
|
||||
v := reflect.Indirect(val)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
st := v.Type()
|
||||
name := st.Field(i).Name
|
||||
if strings.HasPrefix(name, "XXX_") {
|
||||
continue
|
||||
// In the case of criurpc.CriuReqType_FEATURE_CHECK req.GetOpts()
|
||||
// should be empty. For older CRIU versions it still will be
|
||||
// available but empty.
|
||||
if req.GetType() != criurpc.CriuReqType_FEATURE_CHECK {
|
||||
val := reflect.ValueOf(req.GetOpts())
|
||||
v := reflect.Indirect(val)
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
st := v.Type()
|
||||
name := st.Field(i).Name
|
||||
if strings.HasPrefix(name, "XXX_") {
|
||||
continue
|
||||
}
|
||||
value := val.MethodByName("Get" + name).Call([]reflect.Value{})
|
||||
logrus.Debugf("CRIU option %s with value %v", name, value[0])
|
||||
}
|
||||
value := val.MethodByName("Get" + name).Call([]reflect.Value{})
|
||||
logrus.Debugf("CRIU option %s with value %v", name, value[0])
|
||||
}
|
||||
data, err := proto.Marshal(req)
|
||||
if err != nil {
|
||||
|
@ -1063,6 +1126,10 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
|
|||
|
||||
t := resp.GetType()
|
||||
switch {
|
||||
case t == criurpc.CriuReqType_FEATURE_CHECK:
|
||||
logrus.Debugf("Feature check says: %s", resp)
|
||||
criuFeatures = resp.GetFeatures()
|
||||
break
|
||||
case t == criurpc.CriuReqType_NOTIFY:
|
||||
if err := c.criuNotifications(resp, process, opts, extFds); err != nil {
|
||||
return err
|
||||
|
|
Loading…
Reference in New Issue