Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 109dce2

Browse files
utam0klifubang
authored andcommitted
Support process.scheduler
Spec: opencontainers/runtime-spec#1188 Fix: opencontainers#3895 Signed-off-by: utam0k <[email protected]> Signed-off-by: lifubang <[email protected]>
1 parent f235fa6 commit 109dce2

File tree

13 files changed

+221
-2
lines changed

13 files changed

+221
-2
lines changed

docs/spec-conformance.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ v1.0.2 | `.linux.personality` | [#3126](https://github
1313
v1.1.0 | `SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV` | [#3862](https://github.com/opencontainers/runc/pull/3862)
1414
v1.1.0 | time namespaces | [#3876](https://github.com/opencontainers/runc/pull/3876)
1515
v1.1.0 | rsvd hugetlb cgroup | TODO ([#3859](https://github.com/opencontainers/runc/issues/3859))
16-
v1.1.0 | `.process.scheduler` | TODO ([#3895](https://github.com/opencontainers/runc/issues/3895))
1716
v1.1.0 | `.process.ioPriority` | [#3783](https://github.com/opencontainers/runc/pull/3783)
1817

1918

libcontainer/configs/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,14 @@ type Config struct {
219219

220220
// TimeOffsets specifies the offset for supporting time namespaces.
221221
TimeOffsets map[string]specs.LinuxTimeOffset `json:"time_offsets,omitempty"`
222+
223+
// Scheduler represents the scheduling attributes for a process.
224+
Scheduler *Scheduler `json:"scheduler,omitempty"`
222225
}
223226

227+
// Scheduler is based on the Linux sched_setattr(2) syscall.
228+
type Scheduler = specs.Scheduler
229+
224230
type (
225231
HookName string
226232
HookList []Hook

libcontainer/configs/validate/validator.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/opencontainers/runc/libcontainer/cgroups"
1212
"github.com/opencontainers/runc/libcontainer/configs"
1313
"github.com/opencontainers/runc/libcontainer/intelrdt"
14+
"github.com/opencontainers/runtime-spec/specs-go"
1415
selinux "github.com/opencontainers/selinux/go-selinux"
1516
"github.com/sirupsen/logrus"
1617
"golang.org/x/sys/unix"
@@ -30,6 +31,7 @@ func Validate(config *configs.Config) error {
3031
intelrdtCheck,
3132
rootlessEUIDCheck,
3233
mountsStrict,
34+
scheduler,
3335
}
3436
for _, c := range checks {
3537
if err := c(config); err != nil {
@@ -353,3 +355,31 @@ func isHostNetNS(path string) (bool, error) {
353355

354356
return (st1.Dev == st2.Dev) && (st1.Ino == st2.Ino), nil
355357
}
358+
359+
// scheduler is to validate scheduler configs according to https://man7.org/linux/man-pages/man2/sched_setattr.2.html
360+
func scheduler(config *configs.Config) error {
361+
if config.Scheduler == nil {
362+
return nil
363+
}
364+
niceValue := config.Scheduler.Nice
365+
if niceValue < -20 || niceValue > 19 {
366+
return fmt.Errorf("invalid scheduler.nice: %d", niceValue)
367+
}
368+
if config.Scheduler.Policy != specs.SchedFIFO && config.Scheduler.Policy != specs.SchedRR {
369+
if config.Scheduler.Priority != 0 {
370+
return fmt.Errorf("invalid scheduler.priority: %d", config.Scheduler.Priority)
371+
}
372+
}
373+
if config.Scheduler.Policy != specs.SchedDeadline {
374+
if config.Scheduler.Runtime != 0 {
375+
return fmt.Errorf("invalid scheduler.runtime: %d", config.Scheduler.Runtime)
376+
}
377+
if config.Scheduler.Deadline != 0 {
378+
return fmt.Errorf("invalid scheduler.deadline: %d", config.Scheduler.Deadline)
379+
}
380+
if config.Scheduler.Period != 0 {
381+
return fmt.Errorf("invalid scheduler.period: %d", config.Scheduler.Period)
382+
}
383+
}
384+
return nil
385+
}

libcontainer/configs/validate/validator_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,3 +616,52 @@ func TestValidateIDMapMounts(t *testing.T) {
616616
})
617617
}
618618
}
619+
620+
func TestValidateScheduler(t *testing.T) {
621+
testCases := []struct {
622+
isErr bool
623+
policy string
624+
niceValue int32
625+
priority uint32
626+
runtime uint64
627+
deadline uint64
628+
period uint64
629+
}{
630+
{isErr: false, niceValue: 19},
631+
{isErr: false, niceValue: -20},
632+
{isErr: true, niceValue: 20},
633+
{isErr: true, niceValue: -21},
634+
{isErr: true, priority: 100},
635+
{isErr: false, policy: "SCHED_FIFO", priority: 100},
636+
{isErr: true, policy: "SCHED_FIFO", runtime: 20},
637+
{isErr: true, policy: "SCHED_BATCH", deadline: 30},
638+
{isErr: true, policy: "SCHED_IDLE", period: 40},
639+
{isErr: true, policy: "SCHED_DEADLINE", priority: 100},
640+
{isErr: false, policy: "SCHED_DEADLINE", runtime: 200},
641+
{isErr: false, policy: "SCHED_DEADLINE", deadline: 300},
642+
{isErr: false, policy: "SCHED_DEADLINE", period: 400},
643+
}
644+
645+
for _, tc := range testCases {
646+
scheduler := configs.Scheduler{
647+
Policy: specs.LinuxSchedulerPolicy(tc.policy),
648+
Nice: tc.niceValue,
649+
Priority: tc.priority,
650+
Runtime: tc.runtime,
651+
Deadline: tc.deadline,
652+
Period: tc.period,
653+
}
654+
config := &configs.Config{
655+
Rootfs: "/var",
656+
Scheduler: &scheduler,
657+
}
658+
659+
err := Validate(config)
660+
if tc.isErr && err == nil {
661+
t.Errorf("scheduler: %d, expected error, got nil", tc.niceValue)
662+
}
663+
if !tc.isErr && err != nil {
664+
t.Errorf("scheduler: %d, expected nil, got error %v", tc.niceValue, err)
665+
}
666+
}
667+
}

libcontainer/process.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ type Process struct {
9595
//
9696
// For cgroup v2, the only key allowed is "".
9797
SubCgroupPaths map[string]string
98+
99+
Scheduler *configs.Scheduler
98100
}
99101

100102
// Wait waits for the process to exit.

libcontainer/process_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ func (p *setnsProcess) signal(sig os.Signal) error {
8181

8282
func (p *setnsProcess) start() (retErr error) {
8383
defer p.messageSockPair.parent.Close()
84+
8485
// get the "before" value of oom kill count
8586
oom, _ := p.manager.OOMKillCount()
8687
err := p.cmd.Start()

libcontainer/setns_init_linux.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/opencontainers/runc/libcontainer/keys"
1616
"github.com/opencontainers/runc/libcontainer/seccomp"
1717
"github.com/opencontainers/runc/libcontainer/system"
18+
"github.com/opencontainers/runc/libcontainer/utils"
1819
)
1920

2021
// linuxSetnsInit performs the container's initialization for running a new process
@@ -65,6 +66,15 @@ func (l *linuxSetnsInit) Init() error {
6566
unix.Umask(int(*l.config.Config.Umask))
6667
}
6768

69+
if l.config.Config.Scheduler != nil {
70+
if err := unix.SchedSetAttr(0, utils.ToSchedAttr(l.config.Config.Scheduler), 0); err != nil {
71+
if errors.Is(err, unix.EPERM) {
72+
return fmt.Errorf("error setting scheduler(please check you have appropriate privileges or the cpus config): %w", err)
73+
}
74+
return fmt.Errorf("error setting scheduler: %w", err)
75+
}
76+
}
77+
6878
if err := selinux.SetExecLabel(l.config.ProcessLabel); err != nil {
6979
return err
7080
}

libcontainer/specconv/spec_linux.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,17 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
494494
Ambient: spec.Process.Capabilities.Ambient,
495495
}
496496
}
497+
if spec.Process.Scheduler != nil {
498+
config.Scheduler = &configs.Scheduler{
499+
Policy: spec.Process.Scheduler.Policy,
500+
Nice: spec.Process.Scheduler.Nice,
501+
Priority: spec.Process.Scheduler.Priority,
502+
Flags: spec.Process.Scheduler.Flags,
503+
Runtime: spec.Process.Scheduler.Runtime,
504+
Deadline: spec.Process.Scheduler.Deadline,
505+
Period: spec.Process.Scheduler.Period,
506+
}
507+
}
497508
}
498509
createHooks(spec, config)
499510
config.Version = specs.Version

libcontainer/standard_init_linux.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/opencontainers/runc/libcontainer/keys"
1818
"github.com/opencontainers/runc/libcontainer/seccomp"
1919
"github.com/opencontainers/runc/libcontainer/system"
20+
"github.com/opencontainers/runc/libcontainer/utils"
2021
)
2122

2223
type linuxStandardInit struct {
@@ -159,6 +160,16 @@ func (l *linuxStandardInit) Init() error {
159160
return &os.SyscallError{Syscall: "prctl(SET_NO_NEW_PRIVS)", Err: err}
160161
}
161162
}
163+
164+
if l.config.Config.Scheduler != nil {
165+
if err := unix.SchedSetAttr(0, utils.ToSchedAttr(l.config.Config.Scheduler), 0); err != nil {
166+
if errors.Is(err, unix.EPERM) {
167+
return fmt.Errorf("error setting scheduler(please check you have appropriate privileges or the cpus config): %w", err)
168+
}
169+
return fmt.Errorf("error setting scheduler: %w", err)
170+
}
171+
}
172+
162173
// Tell our parent that we're ready to Execv. This must be done before the
163174
// Seccomp rules have been applied, because we need to be able to read and
164175
// write to a socket.

libcontainer/utils/utils_unix.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"strconv"
1111
"sync"
1212

13+
"github.com/opencontainers/runc/libcontainer/configs"
14+
"github.com/opencontainers/runtime-spec/specs-go"
1315
"golang.org/x/sys/unix"
1416
)
1517

@@ -98,3 +100,55 @@ func NewSockPair(name string) (parent, child *os.File, err error) {
98100
}
99101
return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
100102
}
103+
104+
// ToSchedAttr is to convert *configs.Scheduler to *unix.SchedAttr
105+
func ToSchedAttr(scheduler *configs.Scheduler) *unix.SchedAttr {
106+
var policy uint32
107+
switch scheduler.Policy {
108+
case specs.SchedOther:
109+
policy = 0
110+
case specs.SchedFIFO:
111+
policy = 1
112+
case specs.SchedRR:
113+
policy = 2
114+
case specs.SchedBatch:
115+
policy = 3
116+
case specs.SchedISO:
117+
policy = 4
118+
case specs.SchedIdle:
119+
policy = 5
120+
case specs.SchedDeadline:
121+
policy = 6
122+
}
123+
124+
var flags uint64
125+
for _, flag := range scheduler.Flags {
126+
switch flag {
127+
case specs.SchedFlagResetOnFork:
128+
flags |= 0x01
129+
case specs.SchedFlagReclaim:
130+
flags |= 0x02
131+
case specs.SchedFlagDLOverrun:
132+
flags |= 0x04
133+
case specs.SchedFlagKeepPolicy:
134+
flags |= 0x08
135+
case specs.SchedFlagKeepParams:
136+
flags |= 0x10
137+
case specs.SchedFlagUtilClampMin:
138+
flags |= 0x20
139+
case specs.SchedFlagUtilClampMax:
140+
flags |= 0x40
141+
}
142+
}
143+
144+
return &unix.SchedAttr{
145+
Size: unix.SizeofSchedAttr,
146+
Policy: policy,
147+
Flags: flags,
148+
Nice: scheduler.Nice,
149+
Priority: scheduler.Priority,
150+
Runtime: scheduler.Runtime,
151+
Deadline: scheduler.Deadline,
152+
Period: scheduler.Period,
153+
}
154+
}

0 commit comments

Comments
 (0)