mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-07 00:12:53 +08:00
@@ -8,7 +8,6 @@ The following features are not implemented yet:
|
|||||||
Spec version | Feature | PR
|
Spec version | Feature | PR
|
||||||
-------------|------------------------------------------------|----------------------------------------------------------
|
-------------|------------------------------------------------|----------------------------------------------------------
|
||||||
v1.1.0 | `SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV` | [#3862](https://github.com/opencontainers/runc/pull/3862)
|
v1.1.0 | `SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV` | [#3862](https://github.com/opencontainers/runc/pull/3862)
|
||||||
v1.1.0 | `.process.ioPriority` | [#3783](https://github.com/opencontainers/runc/pull/3783)
|
|
||||||
|
|
||||||
## Architectures
|
## Architectures
|
||||||
|
|
||||||
|
@@ -222,6 +222,9 @@ type Config struct {
|
|||||||
|
|
||||||
// Personality contains configuration for the Linux personality syscall.
|
// Personality contains configuration for the Linux personality syscall.
|
||||||
Personality *LinuxPersonality `json:"personality,omitempty"`
|
Personality *LinuxPersonality `json:"personality,omitempty"`
|
||||||
|
|
||||||
|
// IOPriority is the container's I/O priority.
|
||||||
|
IOPriority *IOPriority `json:"io_priority,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scheduler is based on the Linux sched_setattr(2) syscall.
|
// Scheduler is based on the Linux sched_setattr(2) syscall.
|
||||||
@@ -283,6 +286,14 @@ func ToSchedAttr(scheduler *Scheduler) (*unix.SchedAttr, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var IOPrioClassMapping = map[specs.IOPriorityClass]int{
|
||||||
|
specs.IOPRIO_CLASS_RT: 1,
|
||||||
|
specs.IOPRIO_CLASS_BE: 2,
|
||||||
|
specs.IOPRIO_CLASS_IDLE: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IOPriority = specs.LinuxIOPriority
|
||||||
|
|
||||||
type (
|
type (
|
||||||
HookName string
|
HookName string
|
||||||
HookList []Hook
|
HookList []Hook
|
||||||
|
@@ -32,6 +32,7 @@ func Validate(config *configs.Config) error {
|
|||||||
rootlessEUIDCheck,
|
rootlessEUIDCheck,
|
||||||
mountsStrict,
|
mountsStrict,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
ioPriority,
|
||||||
}
|
}
|
||||||
for _, c := range checks {
|
for _, c := range checks {
|
||||||
if err := c(config); err != nil {
|
if err := c(config); err != nil {
|
||||||
@@ -396,3 +397,14 @@ func scheduler(config *configs.Config) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ioPriority(config *configs.Config) error {
|
||||||
|
if config.IOPriority == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
priority := config.IOPriority.Priority
|
||||||
|
if priority < 0 || priority > 7 {
|
||||||
|
return fmt.Errorf("invalid ioPriority.Priority: %d", priority)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -842,3 +842,32 @@ func TestValidateScheduler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateIOPriority(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
isErr bool
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{isErr: false, priority: 0},
|
||||||
|
{isErr: false, priority: 7},
|
||||||
|
{isErr: true, priority: -1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
ioPriroty := configs.IOPriority{
|
||||||
|
Priority: tc.priority,
|
||||||
|
}
|
||||||
|
config := &configs.Config{
|
||||||
|
Rootfs: "/var",
|
||||||
|
IOPriority: &ioPriroty,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Validate(config)
|
||||||
|
if tc.isErr && err == nil {
|
||||||
|
t.Errorf("iopriority: %d, expected error, got nil", tc.priority)
|
||||||
|
}
|
||||||
|
if !tc.isErr && err != nil {
|
||||||
|
t.Errorf("iopriority: %d, expected nil, got error %v", tc.priority, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -100,6 +100,8 @@ type Process struct {
|
|||||||
SubCgroupPaths map[string]string
|
SubCgroupPaths map[string]string
|
||||||
|
|
||||||
Scheduler *configs.Scheduler
|
Scheduler *configs.Scheduler
|
||||||
|
|
||||||
|
IOPriority *configs.IOPriority
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait waits for the process to exit.
|
// Wait waits for the process to exit.
|
||||||
|
@@ -124,6 +124,13 @@ func (p *setnsProcess) signal(sig os.Signal) error {
|
|||||||
|
|
||||||
func (p *setnsProcess) start() (retErr error) {
|
func (p *setnsProcess) start() (retErr error) {
|
||||||
defer p.comm.closeParent()
|
defer p.comm.closeParent()
|
||||||
|
|
||||||
|
if p.process.IOPriority != nil {
|
||||||
|
if err := setIOPriority(p.process.IOPriority); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get the "before" value of oom kill count
|
// get the "before" value of oom kill count
|
||||||
oom, _ := p.manager.OOMKillCount()
|
oom, _ := p.manager.OOMKillCount()
|
||||||
err := p.cmd.Start()
|
err := p.cmd.Start()
|
||||||
@@ -972,3 +979,21 @@ func initWaiter(r io.Reader) chan error {
|
|||||||
|
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setIOPriority(ioprio *configs.IOPriority) error {
|
||||||
|
const ioprioWhoPgrp = 1
|
||||||
|
|
||||||
|
class, ok := configs.IOPrioClassMapping[ioprio.Class]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid io priority class: %s", ioprio.Class)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine class and priority into a single value
|
||||||
|
// https://github.com/torvalds/linux/blob/v5.18/include/uapi/linux/ioprio.h#L5-L17
|
||||||
|
iop := (class << 13) | ioprio.Priority
|
||||||
|
_, _, errno := unix.RawSyscall(unix.SYS_IOPRIO_SET, ioprioWhoPgrp, 0, uintptr(iop))
|
||||||
|
if errno != 0 {
|
||||||
|
return fmt.Errorf("failed to set io priority: %w", errno)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -534,6 +534,11 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||||||
s := *spec.Process.Scheduler
|
s := *spec.Process.Scheduler
|
||||||
config.Scheduler = &s
|
config.Scheduler = &s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if spec.Process.IOPriority != nil {
|
||||||
|
ioPriority := *spec.Process.IOPriority
|
||||||
|
config.IOPriority = &ioPriority
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createHooks(spec, config)
|
createHooks(spec, config)
|
||||||
config.Version = specs.Version
|
config.Version = specs.Version
|
||||||
|
@@ -161,6 +161,11 @@ func (l *linuxStandardInit) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if l.config.Config.IOPriority != nil {
|
||||||
|
if err := setIOPriority(l.config.Config.IOPriority); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tell our parent that we're ready to Execv. This must be done before the
|
// Tell our parent that we're ready to Execv. This must be done before the
|
||||||
// Seccomp rules have been applied, because we need to be able to read and
|
// Seccomp rules have been applied, because we need to be able to read and
|
||||||
|
30
tests/integration/ioprio.bats
Normal file
30
tests/integration/ioprio.bats
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
setup_debian
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
teardown_bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ioprio_set is applied to process group" {
|
||||||
|
# Create a container with a specific I/O priority.
|
||||||
|
update_config '.process.ioPriority = {"class": "IOPRIO_CLASS_BE", "priority": 4}'
|
||||||
|
|
||||||
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_ioprio
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
# Check the init process.
|
||||||
|
runc exec test_ioprio ionice -p 1
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" = *'best-effort: prio 4'* ]]
|
||||||
|
|
||||||
|
# Check the process made from the exec command.
|
||||||
|
runc exec test_ioprio ionice
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
[[ "$output" = *'best-effort: prio 4'* ]]
|
||||||
|
}
|
@@ -67,6 +67,11 @@ func newProcess(p specs.Process) (*libcontainer.Process, error) {
|
|||||||
lp.Scheduler = &s
|
lp.Scheduler = &s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.IOPriority != nil {
|
||||||
|
ioPriority := *p.IOPriority
|
||||||
|
lp.IOPriority = &ioPriority
|
||||||
|
}
|
||||||
|
|
||||||
if p.Capabilities != nil {
|
if p.Capabilities != nil {
|
||||||
lp.Capabilities = &configs.Capabilities{}
|
lp.Capabilities = &configs.Capabilities{}
|
||||||
lp.Capabilities.Bounding = p.Capabilities.Bounding
|
lp.Capabilities.Bounding = p.Capabilities.Bounding
|
||||||
|
Reference in New Issue
Block a user