Skip to content

Commit f4bd1b2

Browse files
authored
feat(envd): give envd realtime IO priority, reset for user processes (#2681)
envd at IOSchedulingClass=realtime + IOWeight=10000; lower io.weight on user/PTY/socat sub-cgroups; user-spawned processes get ioprio reset via the existing wrapper.
1 parent d9e036f commit f4bd1b2

4 files changed

Lines changed: 33 additions & 10 deletions

File tree

packages/envd/internal/services/cgroups/cgroup2.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,34 @@ func createCgroups(configs cgroup2Config) (map[ProcessType]int, error) {
104104
return results, nil
105105
}
106106

107+
// writeCgroupProp writes a cgroupfs property without O_CREATE so missing
108+
// properties error out rather than being silently created on a tmpfs fallback.
109+
func writeCgroupProp(path, value string) error {
110+
f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
111+
if err != nil {
112+
return err
113+
}
114+
defer f.Close()
115+
_, err = f.WriteString(value)
116+
117+
return err
118+
}
119+
107120
func createCgroup(fullPath string, properties map[string]string) (int, error) {
108121
if err := os.MkdirAll(fullPath, 0o755); err != nil {
109122
return -1, fmt.Errorf("failed to create cgroup root: %w", err)
110123
}
111124

112125
var errs []error
113126
for name, value := range properties {
114-
if err := os.WriteFile(filepath.Join(fullPath, name), []byte(value), 0o644); err != nil {
115-
errs = append(errs, fmt.Errorf("failed to write cgroup property: %w", err))
127+
if err := writeCgroupProp(filepath.Join(fullPath, name), value); err != nil {
128+
// Skip properties whose controller isn't enabled in subtree_control.
129+
if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) {
130+
fmt.Fprintf(os.Stderr, "cgroup property %q unavailable at %q, skipping\n", name, fullPath)
131+
132+
continue
133+
}
134+
errs = append(errs, fmt.Errorf("failed to write cgroup property %q: %w", name, err))
116135
}
117136
}
118137
if len(errs) > 0 {

packages/envd/internal/services/process/handler/handler.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,9 @@ func New(
9696
// User command string for logging (without the internal wrapper details).
9797
userCmd := strings.Join(append([]string{req.GetProcess().GetCmd()}, req.GetProcess().GetArgs()...), " ")
9898

99-
// Wrap the command in a shell that sets the OOM score and nice value before exec-ing the actual command.
100-
// This eliminates the race window where grandchildren could inherit the parent's protected OOM score (-1000)
101-
// or high CPU priority (nice -20) before the post-start calls had a chance to correct them.
102-
// nice(1) applies a relative adjustment, so we compute the delta from the current (inherited) nice to the target.
99+
// Wrap in a shell that resets oom_score_adj, ioprio (ionice best-effort/4), and nice.
103100
niceDelta := defaultNice - currentNice()
104-
oomWrapperScript := fmt.Sprintf(`echo %d > /proc/$$/oom_score_adj && exec /usr/bin/nice -n %d "${@}"`, defaultOomScore, niceDelta)
101+
oomWrapperScript := fmt.Sprintf(`echo %d > /proc/$$/oom_score_adj && exec /usr/bin/ionice -c 2 -n 4 /usr/bin/nice -n %d "${@}"`, defaultOomScore, niceDelta)
105102
wrapperArgs := append([]string{"-c", oomWrapperScript, "--", req.GetProcess().GetCmd()}, req.GetProcess().GetArgs()...)
106103
cmd := exec.CommandContext(ctx, "/bin/sh", wrapperArgs...)
107104

packages/envd/main.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,19 +242,22 @@ func createCgroupManager() (m cgroups.Manager) {
242242

243243
opts := []cgroups.Cgroup2ManagerOption{
244244
cgroups.WithCgroup2ProcessType(cgroups.ProcessTypePTY, "ptys", map[string]string{
245-
"cpu.weight": "200", // gets much preferred cpu access, to help keep these real time
245+
"cpu.weight": "200",
246+
"io.weight": "default 50",
246247
"memory.high": fmt.Sprintf("%d", memoryHigh),
247248
"memory.max": fmt.Sprintf("%d", memoryMax),
248249
}),
249250
cgroups.WithCgroup2ProcessType(cgroups.ProcessTypeSocat, "socats", map[string]string{
250-
"cpu.weight": "150", // gets slightly preferred cpu access
251+
"cpu.weight": "150",
252+
"io.weight": "default 50",
251253
"memory.min": fmt.Sprintf("%d", 5*megabyte),
252254
"memory.low": fmt.Sprintf("%d", 8*megabyte),
253255
}),
254256
cgroups.WithCgroup2ProcessType(cgroups.ProcessTypeUser, "user", map[string]string{
255257
"memory.high": fmt.Sprintf("%d", memoryHigh),
256258
"memory.max": fmt.Sprintf("%d", memoryMax),
257-
"cpu.weight": "50", // less than envd, and less than core processes that default to 100
259+
"cpu.weight": "50",
260+
"io.weight": "default 10",
258261
}),
259262
}
260263
if cgroupRoot != "" {

packages/orchestrator/pkg/template/build/core/rootfs/files/envd.service.tpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ LimitCORE=infinity
1717
ExecStartPre=/bin/sh -c 'mountpoint -q /etc/ssl/certs || (mkdir -p /run/e2b/certs && mount --bind /run/e2b/certs /etc/ssl/certs) && ([ -s /etc/ssl/certs/ca-certificates.crt ] || update-ca-certificates)'
1818
ExecStart=/bin/bash -l -c "/usr/bin/envd"
1919
Nice=-20
20+
IOSchedulingClass=realtime
21+
IOSchedulingPriority=4
2022
OOMPolicy=continue
2123
OOMScoreAdjust=-1000
2224
Environment="GOMEMLIMIT={{ .MemoryLimit }}MiB"
@@ -26,6 +28,8 @@ MemoryMin=50M
2628
MemoryLow=100M
2729
CPUAccounting=yes
2830
CPUWeight=1000
31+
IOAccounting=yes
32+
IOWeight=10000
2933

3034
[Install]
3135
WantedBy=multi-user.target

0 commit comments

Comments
 (0)