Skip to content

Commit e305910

Browse files
authored
Merge pull request #60 from adrianreber/2021-12-16-feature-check
Add feature checking support to go-criu
2 parents bbf788d + 200ac0c commit e305910

File tree

4 files changed

+130
-6
lines changed

4 files changed

+130
-6
lines changed

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ SHELL = /bin/bash
22
GO ?= go
33
CC ?= gcc
44
COVERAGE_PATH ?= $(shell pwd)/.coverage
5+
CRIU_FEATURE_MEM_TRACK = $(shell if criu check --feature mem_dirty_track > /dev/null; then echo 1; else echo 0; fi)
6+
CRIU_FEATURE_LAZY_PAGES = $(shell if criu check --feature uffd-noncoop > /dev/null; then echo 1; else echo 0; fi)
7+
CRIU_FEATURE_PIDFD_STORE = $(shell if criu check --feature pidfd_store > /dev/null; then echo 1; else echo 0; fi)
8+
9+
export CRIU_FEATURE_MEM_TRACK CRIU_FEATURE_LAZY_PAGES CRIU_FEATURE_PIDFD_STORE
510

611
all: build test phaul-test
712

@@ -70,6 +75,8 @@ coverage: $(COVERAGE_BINARIES) $(TEST_PAYLOAD)
7075
test/phaul/phaul.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE $$PID; \
7176
pkill -9 piggie; \
7277
}
78+
echo "mode: set" > .coverage/coverage.out && cat .coverage/coverprofile* | \
79+
grep -v mode: | sort -r | awk '{if($$1 != last) {print $$0;last=$$1}}' >> .coverage/coverage.out
7380

7481
clean:
7582
@rm -f $(TEST_BINARIES) $(COVERAGE_BINARIES) codecov
@@ -95,6 +102,6 @@ vendor:
95102
codecov:
96103
curl -Os https://uploader.codecov.io/latest/linux/codecov
97104
chmod +x codecov
98-
./codecov -f '.coverage/*'
105+
./codecov -f '.coverage/coverage.out'
99106

100107
.PHONY: build test phaul-test test-bin clean lint vendor coverage codecov

features.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package criu
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/checkpoint-restore/go-criu/v5/rpc"
7+
)
8+
9+
// Feature checking in go-criu is based on the libcriu feature checking function.
10+
11+
// Feature checking allows the user to check if CRIU supports
12+
// certain features. There are CRIU features which do not depend
13+
// on the version of CRIU but on kernel features or architecture.
14+
//
15+
// One example is memory tracking. Memory tracking can be disabled
16+
// in the kernel or there are architectures which do not support
17+
// it (aarch64 for example). By using the feature check a libcriu
18+
// user can easily query CRIU if a certain feature is available.
19+
//
20+
// The features which should be checked can be marked in the
21+
// structure 'struct criu_feature_check'. Each structure member
22+
// that is set to true will result in CRIU checking for the
23+
// availability of that feature in the current combination of
24+
// CRIU/kernel/architecture.
25+
//
26+
// Available features will be set to true when the function
27+
// returns successfully. Missing features will be set to false.
28+
29+
func (c *Criu) FeatureCheck(features *rpc.CriuFeatures) (*rpc.CriuFeatures, error) {
30+
resp, err := c.doSwrkWithResp(
31+
rpc.CriuReqType_FEATURE_CHECK,
32+
nil,
33+
nil,
34+
features,
35+
)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
if resp.GetType() != rpc.CriuReqType_FEATURE_CHECK {
41+
return nil, fmt.Errorf("Unexpected CRIU RPC response")
42+
}
43+
44+
return features, nil
45+
}

main.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,19 +87,19 @@ func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
8787
}
8888

8989
func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
90-
resp, err := c.doSwrkWithResp(reqType, opts, nfy)
90+
resp, err := c.doSwrkWithResp(reqType, opts, nfy, nil)
9191
if err != nil {
9292
return err
9393
}
9494
respType := resp.GetType()
9595
if respType != reqType {
96-
return errors.New("unexpected responce")
96+
return errors.New("unexpected CRIU RPC response")
9797
}
9898

9999
return nil
100100
}
101101

102-
func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) (*rpc.CriuResp, error) {
102+
func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify, features *rpc.CriuFeatures) (*rpc.CriuResp, error) {
103103
var resp *rpc.CriuResp
104104

105105
req := rpc.CriuReq{
@@ -111,6 +111,10 @@ func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy N
111111
opts.NotifyScripts = proto.Bool(true)
112112
}
113113

114+
if features != nil {
115+
req.Features = features
116+
}
117+
114118
if c.swrkCmd == nil {
115119
err := c.Prepare()
116120
if err != nil {
@@ -209,7 +213,7 @@ func (c *Criu) StartPageServer(opts *rpc.CriuOpts) error {
209213

210214
// StartPageServerChld starts the page server and returns PID and port
211215
func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
212-
resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil)
216+
resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil, nil)
213217
if err != nil {
214218
return 0, 0, err
215219
}
@@ -220,7 +224,7 @@ func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
220224
// GetCriuVersion executes the VERSION RPC call and returns the version
221225
// as an integer. Major * 10000 + Minor * 100 + SubLevel
222226
func (c *Criu) GetCriuVersion() (int, error) {
223-
resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil)
227+
resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil, nil)
224228
if err != nil {
225229
return 0, err
226230
}

test/main.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,69 @@ func doDump(c *criu.Criu, pidS string, imgDir string, pre bool, prevImg string)
5858
return nil
5959
}
6060

61+
func featureCheck(c *criu.Criu) error {
62+
features := &rpc.CriuFeatures{}
63+
featuresToCompare := &rpc.CriuFeatures{}
64+
env := os.Getenv("CRIU_FEATURE_MEM_TRACK")
65+
if env != "" {
66+
val, err := strconv.Atoi(env)
67+
if err != nil {
68+
return err
69+
}
70+
features.MemTrack = proto.Bool(val != 0)
71+
featuresToCompare.MemTrack = proto.Bool(val != 0)
72+
}
73+
env = os.Getenv("CRIU_FEATURE_LAZY_PAGES")
74+
if env != "" {
75+
val, err := strconv.Atoi(env)
76+
if err != nil {
77+
return err
78+
}
79+
features.LazyPages = proto.Bool(val != 0)
80+
featuresToCompare.LazyPages = proto.Bool(val != 0)
81+
}
82+
env = os.Getenv("CRIU_FEATURE_PIDFD_STORE")
83+
if env != "" {
84+
val, err := strconv.Atoi(env)
85+
if err != nil {
86+
return err
87+
}
88+
features.PidfdStore = proto.Bool(val != 0)
89+
featuresToCompare.PidfdStore = proto.Bool(val != 0)
90+
}
91+
92+
features, err := c.FeatureCheck(features)
93+
if err != nil {
94+
return err
95+
}
96+
97+
if *features.MemTrack != *featuresToCompare.MemTrack {
98+
return fmt.Errorf(
99+
"Unexpected MemTrack FeatureCheck result %v:%v",
100+
*features.MemTrack,
101+
*featuresToCompare.MemTrack,
102+
)
103+
}
104+
105+
if *features.LazyPages != *featuresToCompare.LazyPages {
106+
return fmt.Errorf(
107+
"Unexpected LazyPages FeatureCheck result %v:%v",
108+
*features.LazyPages,
109+
*featuresToCompare.LazyPages,
110+
)
111+
}
112+
113+
if *features.PidfdStore != *featuresToCompare.PidfdStore {
114+
return fmt.Errorf(
115+
"Unexpected PidfdStore FeatureCheck result %v:%v",
116+
*features.PidfdStore,
117+
*featuresToCompare.PidfdStore,
118+
)
119+
}
120+
121+
return nil
122+
}
123+
61124
// Usage: test $act $pid $images_dir
62125
func main() {
63126
c := criu.MakeCriu()
@@ -76,6 +139,11 @@ func main() {
76139
if !result {
77140
log.Fatalln("CRIU version to old")
78141
}
142+
143+
if err = featureCheck(c); err != nil {
144+
log.Fatalln(err)
145+
}
146+
79147
act := os.Args[1]
80148
switch act {
81149
case "dump":

0 commit comments

Comments
 (0)