Skip to content

Commit fc75aa0

Browse files
committed
Add numa cpuset support
This change adds support for specifying a set of cpu and memory nodes during the CreateVM call. This will run the jailed process on those set of cpus and restrict access to the specified nodes. This change also adds the cpuset.Builder which allows for easy creation of cpus and mems strings Signed-off-by: xibz <[email protected]>
1 parent 3b71606 commit fc75aa0

File tree

9 files changed

+430
-49
lines changed

9 files changed

+430
-49
lines changed

proto/firecracker.pb.go

Lines changed: 61 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/firecracker.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,12 @@ message GetVMMetadataResponse {
8181

8282
message JailerConfig {
8383
string NetNS = 1;
84+
85+
// The list of CPUs to run the jailed application on. This field can only
86+
// be used on numa architectures
87+
string CPUs = 2;
88+
// The list of memory nodes to run with the jailed application on. This
89+
// field can only be used on numa architectures
90+
string Mems = 3;
8491
}
8592

runtime/cpuset/cpuset_builder.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package cpuset
15+
16+
import (
17+
"fmt"
18+
)
19+
20+
// Builder will allow building of cpuset fields
21+
// http://man7.org/linux/man-pages/man7/cpuset.7.html
22+
type Builder struct {
23+
cpus []int
24+
cpuRanges []_range
25+
26+
mems []int
27+
memRanges []_range
28+
}
29+
30+
type _range struct {
31+
min, max int
32+
}
33+
34+
// CPUSet represents the linux CPUSet which is a series of configurable values
35+
// that allow processes to run on a specific CPUs and those CPUs are then bound
36+
// to the memory nodes specified.
37+
type CPUSet struct {
38+
cpus string
39+
mems string
40+
}
41+
42+
// CPUs returns the cpuset.cpus string
43+
func (c CPUSet) CPUs() string {
44+
return c.cpus
45+
}
46+
47+
// Mems returns the cpuset.mems string
48+
func (c CPUSet) Mems() string {
49+
return c.mems
50+
}
51+
52+
// AddCPU will add the physical CPU number that the process is allowed to run
53+
// on.
54+
func (b Builder) AddCPU(cpu int) Builder {
55+
b.cpus = append(b.cpus, cpu)
56+
return b
57+
}
58+
59+
// AddCPURange adds a range of physical CPU numbers that the process is allowed
60+
// to run on
61+
func (b Builder) AddCPURange(min, max int) Builder {
62+
b.cpuRanges = append(b.cpuRanges, _range{
63+
min: min,
64+
max: max,
65+
})
66+
67+
return b
68+
}
69+
70+
// AddMem adds a memory node which limit where the cpus can allocate memory
71+
func (b Builder) AddMem(mem int) Builder {
72+
b.mems = append(b.mems, mem)
73+
return b
74+
}
75+
76+
// AddMemRange adds a range of memory nodes by utilizing the minimum node to
77+
// use to the maximum node to use.
78+
func (b Builder) AddMemRange(min, max int) Builder {
79+
b.memRanges = append(b.memRanges, _range{
80+
min: min,
81+
max: max,
82+
})
83+
84+
return b
85+
}
86+
87+
// Build constructs a new CPUSet
88+
func (b Builder) Build() CPUSet {
89+
cpus := stringify(b.cpus, b.cpuRanges)
90+
mems := stringify(b.mems, b.memRanges)
91+
92+
return CPUSet{
93+
cpus: cpus,
94+
mems: mems,
95+
}
96+
}
97+
98+
func stringify(elems []int, ranges []_range) string {
99+
str := ""
100+
for _, elem := range elems {
101+
sep := ","
102+
if len(str) == 0 {
103+
sep = ""
104+
}
105+
106+
str += fmt.Sprintf("%s%d", sep, elem)
107+
}
108+
109+
for _, r := range ranges {
110+
sep := ","
111+
if len(str) == 0 {
112+
sep = ""
113+
}
114+
115+
str += fmt.Sprintf("%s%d-%d", sep, r.min, r.max)
116+
}
117+
118+
return str
119+
}

runtime/cpuset/cpuset_builder_test.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package cpuset
15+
16+
import (
17+
"testing"
18+
19+
"github.com/stretchr/testify/assert"
20+
)
21+
22+
func TestCPUSet(t *testing.T) {
23+
cases := []struct {
24+
name string
25+
cpus []int
26+
mems []int
27+
cpuRanges []_range
28+
memRanges []_range
29+
expectedCPUSet CPUSet
30+
}{
31+
{
32+
name: "single cpu only",
33+
cpus: []int{0},
34+
expectedCPUSet: CPUSet{
35+
cpus: "0",
36+
},
37+
},
38+
{
39+
name: "cpus only",
40+
cpus: []int{0, 5, 6},
41+
expectedCPUSet: CPUSet{
42+
cpus: "0,5,6",
43+
},
44+
},
45+
{
46+
name: "single mem only",
47+
mems: []int{2},
48+
expectedCPUSet: CPUSet{
49+
mems: "2",
50+
},
51+
},
52+
{
53+
name: "mems only",
54+
mems: []int{2, 8, 3},
55+
expectedCPUSet: CPUSet{
56+
mems: "2,8,3",
57+
},
58+
},
59+
{
60+
name: "cpu single range only",
61+
cpuRanges: []_range{
62+
{
63+
min: 0,
64+
max: 3,
65+
},
66+
},
67+
expectedCPUSet: CPUSet{
68+
cpus: "0-3",
69+
},
70+
},
71+
{
72+
name: "cpu ranges only",
73+
cpuRanges: []_range{
74+
{
75+
min: 0,
76+
max: 3,
77+
},
78+
{
79+
min: 5,
80+
max: 10,
81+
},
82+
},
83+
expectedCPUSet: CPUSet{
84+
cpus: "0-3,5-10",
85+
},
86+
},
87+
{
88+
name: "mem single range only",
89+
memRanges: []_range{
90+
{
91+
min: 0,
92+
max: 1,
93+
},
94+
},
95+
expectedCPUSet: CPUSet{
96+
mems: "0-1",
97+
},
98+
},
99+
{
100+
name: "mem ranges only",
101+
memRanges: []_range{
102+
{
103+
min: 0,
104+
max: 1,
105+
},
106+
{
107+
min: 2,
108+
max: 3,
109+
},
110+
},
111+
expectedCPUSet: CPUSet{
112+
mems: "0-1,2-3",
113+
},
114+
},
115+
{
116+
name: "all inclusive",
117+
cpus: []int{15, 17, 31},
118+
cpuRanges: []_range{
119+
{
120+
min: 0,
121+
max: 3,
122+
},
123+
},
124+
mems: []int{128, 131, 140},
125+
memRanges: []_range{
126+
{
127+
min: 0,
128+
max: 1,
129+
},
130+
{
131+
min: 2,
132+
max: 3,
133+
},
134+
},
135+
expectedCPUSet: CPUSet{
136+
cpus: "15,17,31,0-3",
137+
mems: "128,131,140,0-1,2-3",
138+
},
139+
},
140+
}
141+
142+
for _, _c := range cases {
143+
c := _c
144+
t.Run(c.name, func(t *testing.T) {
145+
b := Builder{}
146+
for _, cpu := range c.cpus {
147+
b = b.AddCPU(cpu)
148+
}
149+
150+
for _, r := range c.cpuRanges {
151+
b = b.AddCPURange(r.min, r.max)
152+
}
153+
154+
for _, mem := range c.mems {
155+
b = b.AddMem(mem)
156+
}
157+
158+
for _, r := range c.memRanges {
159+
b = b.AddMemRange(r.min, r.max)
160+
}
161+
162+
cpuSet := b.Build()
163+
assert.Equal(t, c.expectedCPUSet, cpuSet)
164+
})
165+
}
166+
}

0 commit comments

Comments
 (0)