Add option to support criu manage cgroups mode for dump and restore

CRIU supports cgroup-manage mode from v1.7

Signed-off-by: Hui Kang <hkang.sunysb@gmail.com>
This commit is contained in:
Hui Kang 2015-08-06 15:14:59 +00:00 committed by root
parent 76674393ef
commit 25da513c4b
6 changed files with 237 additions and 50 deletions

View File

@ -23,6 +23,7 @@ var checkpointCommand = cli.Command{
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"}, cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"}, cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"}, cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'."},
}, },
Action: func(context *cli.Context) { Action: func(context *cli.Context) {
container, err := getContainer(context) container, err := getContainer(context)
@ -39,6 +40,7 @@ var checkpointCommand = cli.Command{
} }
// these are the mandatory criu options for a container // these are the mandatory criu options for a container
setPageServer(context, options) setPageServer(context, options)
setManageCgroupsMode(context, options)
if err := container.Checkpoint(options); err != nil { if err := container.Checkpoint(options); err != nil {
fatal(err) fatal(err)
} }
@ -71,3 +73,18 @@ func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
} }
} }
} }
func setManageCgroupsMode(context *cli.Context, options *libcontainer.CriuOpts) {
if cgOpt := context.String("manage-cgroups-mode"); cgOpt != "" {
switch cgOpt {
case "soft":
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_SOFT
case "full":
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_FULL
case "strict":
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_STRICT
default:
fatal(fmt.Errorf("Invalid manage cgroups mode"))
}
}
}

View File

@ -289,14 +289,45 @@ func addArgsFromEnv(evar string, args *[]string) {
fmt.Printf(">>> criu %v\n", *args) fmt.Printf(">>> criu %v\n", *args)
} }
func (c *linuxContainer) checkCriuVersion() error { // check Criu version greater than or equal to min_version
var x, y, z int func (c *linuxContainer) checkCriuVersion(min_version string) error {
var x, y, z, versionReq int
_, err := fmt.Sscanf(min_version, "%d.%d.%d\n", &x, &y, &z) // 1.5.2
if err != nil {
_, err = fmt.Sscanf(min_version, "Version: %d.%d\n", &x, &y) // 1.6
}
versionReq = x*10000 + y*100 + z
out, err := exec.Command(c.criuPath, "-V").Output() out, err := exec.Command(c.criuPath, "-V").Output()
if err != nil { if err != nil {
return fmt.Errorf("Unable to execute CRIU command: %s", c.criuPath) return fmt.Errorf("Unable to execute CRIU command: %s", c.criuPath)
} }
x = 0
y = 0
z = 0
if ep := strings.Index(string(out), "-"); ep >= 0 {
// criu Git version format
var version string
if sp := strings.Index(string(out), "GitID"); sp > 0 {
version = string(out)[sp:ep]
} else {
return fmt.Errorf("Unable to parse the CRIU version: %s", c.criuPath)
}
n, err := fmt.Sscanf(string(version), "GitID: v%d.%d.%d", &x, &y, &z) // 1.5.2
if err != nil {
n, err = fmt.Sscanf(string(version), "GitID: v%d.%d", &x, &y) // 1.6
y++
} else {
z++
}
if n < 2 || err != nil {
return fmt.Errorf("Unable to parse the CRIU version: %s %d %s", version, n, err)
}
} else {
// criu release version format
n, err := fmt.Sscanf(string(out), "Version: %d.%d.%d\n", &x, &y, &z) // 1.5.2 n, err := fmt.Sscanf(string(out), "Version: %d.%d.%d\n", &x, &y, &z) // 1.5.2
if err != nil { if err != nil {
n, err = fmt.Sscanf(string(out), "Version: %d.%d\n", &x, &y) // 1.6 n, err = fmt.Sscanf(string(out), "Version: %d.%d\n", &x, &y) // 1.6
@ -304,9 +335,10 @@ func (c *linuxContainer) checkCriuVersion() error {
if n < 2 || err != nil { if n < 2 || err != nil {
return fmt.Errorf("Unable to parse the CRIU version: %s %d %s", out, n, err) return fmt.Errorf("Unable to parse the CRIU version: %s %d %s", out, n, err)
} }
}
if x*10000+y*100+z < 10502 { if x*10000+y*100+z < versionReq {
return fmt.Errorf("CRIU version must be 1.5.2 or higher") return fmt.Errorf("CRIU version must be %s or higher", min_version)
} }
return nil return nil
@ -331,7 +363,7 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
if err := c.checkCriuVersion(); err != nil { if err := c.checkCriuVersion("1.5.2"); err != nil {
return err return err
} }
@ -389,6 +421,14 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
} }
} }
// append optional manage cgroups mode
if criuOpts.ManageCgroupsMode != 0 {
if err := c.checkCriuVersion("1.7"); err != nil {
return err
}
rpcOpts.ManageCgroupsMode = proto.Uint32(uint32(criuOpts.ManageCgroupsMode))
}
t := criurpc.CriuReqType_DUMP t := criurpc.CriuReqType_DUMP
req := &criurpc.CriuReq{ req := &criurpc.CriuReq{
Type: &t, Type: &t,
@ -448,7 +488,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
if err := c.checkCriuVersion(); err != nil { if err := c.checkCriuVersion("1.5.2"); err != nil {
return err return err
} }
@ -553,6 +593,14 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
req.Opts.Veths = append(req.Opts.Veths, veth) req.Opts.Veths = append(req.Opts.Veths, veth)
} }
// append optional manage cgroups mode
if criuOpts.ManageCgroupsMode != 0 {
if err := c.checkCriuVersion("1.7"); err != nil {
return err
}
req.Opts.ManageCgroupsMode = proto.Uint32(uint32(criuOpts.ManageCgroupsMode))
}
var ( var (
fds []string fds []string
fdJSON []byte fdJSON []byte

View File

@ -1,5 +1,15 @@
package libcontainer package libcontainer
// cgroup restoring strategy provided by criu
type cg_mode uint32
const (
CRIU_CG_MODE_SOFT cg_mode = 3 + iota // restore cgroup properties if only dir created by criu
CRIU_CG_MODE_FULL // always restore all cgroups and their properties
CRIU_CG_MODE_STRICT // restore all, requiring them to not present in the system
CRIU_CG_MODE_DEFAULT // the same as CRIU_CG_MODE_SOFT
)
type CriuPageServerInfo struct { type CriuPageServerInfo struct {
Address string // IP address of CRIU page server Address string // IP address of CRIU page server
Port int32 // port number of CRIU page server Port int32 // port number of CRIU page server
@ -20,4 +30,5 @@ type CriuOpts struct {
FileLocks bool // handle file locks, for safety FileLocks bool // handle file locks, for safety
PageServer CriuPageServerInfo // allow to dump to criu page server PageServer CriuPageServerInfo // allow to dump to criu page server
VethPairs []VethPairName // pass the veth to criu when restore VethPairs []VethPairName // pass the veth to criu when restore
ManageCgroupsMode cg_mode // dump or restore cgroup mode
} }

View File

@ -2,15 +2,33 @@
// source: criurpc.proto // source: criurpc.proto
// DO NOT EDIT! // DO NOT EDIT!
/*
Package criurpc is a generated protocol buffer package.
It is generated from these files:
criurpc.proto
It has these top-level messages:
CriuPageServerInfo
CriuVethPair
ExtMountMap
InheritFd
CgroupRoot
UnixSk
CriuOpts
CriuDumpResp
CriuRestoreResp
CriuNotify
CriuReq
CriuResp
*/
package criurpc package criurpc
import proto "github.com/golang/protobuf/proto" import proto "github.com/golang/protobuf/proto"
import json "encoding/json"
import math "math" import math "math"
// Reference proto, json, and math imports to suppress error if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto.Marshal
var _ = &json.SyntaxError{}
var _ = math.Inf var _ = math.Inf
type CriuReqType int32 type CriuReqType int32
@ -58,9 +76,6 @@ func (x CriuReqType) Enum() *CriuReqType {
func (x CriuReqType) String() string { func (x CriuReqType) String() string {
return proto.EnumName(CriuReqType_name, int32(x)) return proto.EnumName(CriuReqType_name, int32(x))
} }
func (x CriuReqType) MarshalJSON() ([]byte, error) {
return json.Marshal(x.String())
}
func (x *CriuReqType) UnmarshalJSON(data []byte) error { func (x *CriuReqType) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(CriuReqType_value, data, "CriuReqType") value, err := proto.UnmarshalJSONEnum(CriuReqType_value, data, "CriuReqType")
if err != nil { if err != nil {
@ -206,6 +221,22 @@ func (m *CgroupRoot) GetPath() string {
return "" return ""
} }
type UnixSk struct {
Inode *uint32 `protobuf:"varint,1,req,name=inode" json:"inode,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *UnixSk) Reset() { *m = UnixSk{} }
func (m *UnixSk) String() string { return proto.CompactTextString(m) }
func (*UnixSk) ProtoMessage() {}
func (m *UnixSk) GetInode() uint32 {
if m != nil && m.Inode != nil {
return *m.Inode
}
return 0
}
type CriuOpts struct { type CriuOpts struct {
ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd" json:"images_dir_fd,omitempty"` ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd" json:"images_dir_fd,omitempty"`
Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"`
@ -234,6 +265,13 @@ type CriuOpts struct {
CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root" json:"cg_root,omitempty"` CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root" json:"cg_root,omitempty"`
RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling" json:"rst_sibling,omitempty"` RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling" json:"rst_sibling,omitempty"`
InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd" json:"inherit_fd,omitempty"` InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd" json:"inherit_fd,omitempty"`
AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt" json:"auto_ext_mnt,omitempty"`
ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing" json:"ext_sharing,omitempty"`
ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters" json:"ext_masters,omitempty"`
SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt" json:"skip_mnt,omitempty"`
EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs" json:"enable_fs,omitempty"`
UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino" json:"unix_sk_ino,omitempty"`
ManageCgroupsMode *uint32 `protobuf:"varint,34,opt,name=manage_cgroups_mode" json:"manage_cgroups_mode,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -433,6 +471,55 @@ func (m *CriuOpts) GetInheritFd() []*InheritFd {
return nil return nil
} }
func (m *CriuOpts) GetAutoExtMnt() bool {
if m != nil && m.AutoExtMnt != nil {
return *m.AutoExtMnt
}
return false
}
func (m *CriuOpts) GetExtSharing() bool {
if m != nil && m.ExtSharing != nil {
return *m.ExtSharing
}
return false
}
func (m *CriuOpts) GetExtMasters() bool {
if m != nil && m.ExtMasters != nil {
return *m.ExtMasters
}
return false
}
func (m *CriuOpts) GetSkipMnt() []string {
if m != nil {
return m.SkipMnt
}
return nil
}
func (m *CriuOpts) GetEnableFs() []string {
if m != nil {
return m.EnableFs
}
return nil
}
func (m *CriuOpts) GetUnixSkIno() []*UnixSk {
if m != nil {
return m.UnixSkIno
}
return nil
}
func (m *CriuOpts) GetManageCgroupsMode() uint32 {
if m != nil && m.ManageCgroupsMode != nil {
return *m.ManageCgroupsMode
}
return 0
}
type CriuDumpResp struct { type CriuDumpResp struct {
Restored *bool `protobuf:"varint,1,opt,name=restored" json:"restored,omitempty"` Restored *bool `protobuf:"varint,1,opt,name=restored" json:"restored,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -509,7 +596,7 @@ func (m *CriuReq) GetType() CriuReqType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return 0 return CriuReqType_EMPTY
} }
func (m *CriuReq) GetOpts() *CriuOpts { func (m *CriuReq) GetOpts() *CriuOpts {
@ -552,7 +639,7 @@ func (m *CriuResp) GetType() CriuReqType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return 0 return CriuReqType_EMPTY
} }
func (m *CriuResp) GetSuccess() bool { func (m *CriuResp) GetSuccess() bool {

View File

@ -25,6 +25,10 @@ message cgroup_root {
required string path = 2; required string path = 2;
}; };
message unix_sk {
required uint32 inode = 1;
};
message criu_opts { message criu_opts {
required int32 images_dir_fd = 1; required int32 images_dir_fd = 1;
optional int32 pid = 2; /* if not set on dump, will dump requesting process */ optional int32 pid = 2; /* if not set on dump, will dump requesting process */
@ -56,11 +60,22 @@ message criu_opts {
repeated string exec_cmd = 22; repeated string exec_cmd = 22;
repeated ext_mount_map ext_mnt = 23; repeated ext_mount_map ext_mnt = 23;
optional bool manage_cgroups = 24; optional bool manage_cgroups = 24; /* backward compatibility */
repeated cgroup_root cg_root = 25; repeated cgroup_root cg_root = 25;
optional bool rst_sibling = 26; /* swrk only */ optional bool rst_sibling = 26; /* swrk only */
repeated inherit_fd inherit_fd = 27; repeated inherit_fd inherit_fd = 27; /* swrk only */
optional bool auto_ext_mnt = 28;
optional bool ext_sharing = 29;
optional bool ext_masters = 30;
repeated string skip_mnt = 31;
repeated string enable_fs = 32;
repeated unix_sk unix_sk_ino = 33;
optional uint32 manage_cgroups_mode = 34;
} }
message criu_dump_resp { message criu_dump_resp {

View File

@ -44,6 +44,11 @@ var restoreCommand = cli.Command{
Name: "file-locks", Name: "file-locks",
Usage: "handle file locks, for safety", Usage: "handle file locks, for safety",
}, },
cli.StringFlag{
Name: "manage-cgroups-mode",
Value: "",
Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'.",
},
cli.StringFlag{ cli.StringFlag{
Name: "config-file, c", Name: "config-file, c",
Value: "config.json", Value: "config.json",
@ -90,6 +95,7 @@ func restoreContainer(context *cli.Context, spec *specs.LinuxSpec, config *confi
} }
} }
options := criuOptions(context) options := criuOptions(context)
status, err := container.Status() status, err := container.Status()
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
@ -97,6 +103,9 @@ func restoreContainer(context *cli.Context, spec *specs.LinuxSpec, config *confi
if status == libcontainer.Running { if status == libcontainer.Running {
fatal(fmt.Errorf("Container with id %s already running", context.GlobalString("id"))) fatal(fmt.Errorf("Container with id %s already running", context.GlobalString("id")))
} }
setManageCgroupsMode(context, options)
// ensure that the container is always removed if we were the process // ensure that the container is always removed if we were the process
// that created it. // that created it.
defer func() { defer func() {