Skip to content

Commit

Permalink
Add I/O priority
Browse files Browse the repository at this point in the history
Signed-off-by: utam0k <k0ma@utam0k.jp>
  • Loading branch information
utam0k committed Jun 12, 2023
1 parent 0b9d545 commit 5994674
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 5 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/godbus/dbus/v5 v5.1.0
github.com/moby/sys/mountinfo v0.6.2
github.com/mrunalp/fileutils v0.5.0
github.com/opencontainers/runtime-spec v1.1.0-rc.2
github.com/opencontainers/runtime-spec v1.1.0-rc.3
github.com/opencontainers/selinux v1.11.0
github.com/seccomp/libseccomp-golang v0.10.0
github.com/sirupsen/logrus v1.9.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vyg
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/opencontainers/runtime-spec v1.1.0-rc.2 h1:ucBtEms2tamYYW/SvGpvq9yUN0NEVL6oyLEwDcTSrk8=
github.com/opencontainers/runtime-spec v1.1.0-rc.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.1.0-rc.3 h1:l04uafi6kxByhbxev7OWiuUv0LZxEsYUfDWZ6bztAuU=
github.com/opencontainers/runtime-spec v1.1.0-rc.3/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
14 changes: 14 additions & 0 deletions libcontainer/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,20 @@ type Config struct {
// RootlessCgroups is set when unlikely to have the full access to cgroups.
// When RootlessCgroups is set, cgroups errors are ignored.
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`

// IOPriority is the container's I/O priority.
IOPriority *IOPriority `json:"io_priority,omitempty"`
}

var IOPrioClassMapping = map[specs.IOPriorityClass]int{
specs.IOPRIO_CLASS_RT: 1,
specs.IOPRIO_CLASS_BE: 2,
specs.IOPRIO_CLASS_IDLE: 3,
}

type IOPriority struct {
Class specs.IOPriorityClass `json:"class"`
Priority int `json:"priority"`
}

type (
Expand Down
13 changes: 13 additions & 0 deletions libcontainer/configs/validate/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func Validate(config *configs.Config) error {
sysctl,
intelrdtCheck,
rootlessEUIDCheck,
ioPriority,
}
for _, c := range checks {
if err := c(config); err != nil {
Expand Down Expand Up @@ -286,3 +287,15 @@ func isHostNetNS(path string) (bool, error) {

return (st1.Dev == st2.Dev) && (st1.Ino == st2.Ino), nil
}

func ioPriority(config *configs.Config) error {
ioPriroty := config.IOPriority
if ioPriroty == nil {
return nil
}
priority := config.IOPriority.Priority
if priority < 0 || priority > 7 {
return fmt.Errorf("invalid ioPriority.Priority: %d", priority)
}
return nil
}
29 changes: 29 additions & 0 deletions libcontainer/configs/validate/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,32 @@ func TestValidateMounts(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("iopriroty: %d, expected error, got nil", tc.priority)
}
if !tc.isErr && err != nil {
t.Errorf("iopriroty: %d, expected nil, got error %v", tc.priority, err)
}
}
}
2 changes: 2 additions & 0 deletions libcontainer/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ type Process struct {
//
// For cgroup v2, the only key allowed is "".
SubCgroupPaths map[string]string

IOPriority *configs.IOPriority
}

// Wait waits for the process to exit.
Expand Down
6 changes: 6 additions & 0 deletions libcontainer/process_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ func (p *setnsProcess) signal(sig os.Signal) error {

func (p *setnsProcess) start() (retErr error) {
defer p.messageSockPair.parent.Close()
if p.process.IOPriority != nil {
if err := utils.SetIOPriority(p.process.IOPriority); err != nil {
return fmt.Errorf("unable to apply iopriority: %w", err)
}
}

// get the "before" value of oom kill count
oom, _ := p.manager.OOMKillCount()
err := p.cmd.Start()
Expand Down
6 changes: 6 additions & 0 deletions libcontainer/specconv/spec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,12 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
Ambient: spec.Process.Capabilities.Ambient,
}
}
if spec.Process.IOPriority != nil {
config.IOPriority = &configs.IOPriority{
Class: spec.Process.IOPriority.Class,
Priority: spec.Process.IOPriority.Priority,
}
}
}
createHooks(spec, config)
config.Version = specs.Version
Expand Down
8 changes: 8 additions & 0 deletions libcontainer/standard_init_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/opencontainers/runc/libcontainer/keys"
"github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/utils"
)

type linuxStandardInit struct {
Expand Down Expand Up @@ -159,6 +160,13 @@ func (l *linuxStandardInit) Init() error {
return &os.SyscallError{Syscall: "prctl(SET_NO_NEW_PRIVS)", Err: err}
}
}

if l.config.Config.IOPriority != nil {
if err := utils.SetIOPriority(l.config.Config.IOPriority); err != nil {
return fmt.Errorf("unable to apply iopriority: %w", err)
}
}

// 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
// write to a socket.
Expand Down
22 changes: 22 additions & 0 deletions libcontainer/utils/utils_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"fmt"
"os"
"strconv"
"syscall"

"github.com/opencontainers/runc/libcontainer/configs"

"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -67,3 +70,22 @@ func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
}
return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
}

const (
IoprioWhoPgrp = 1
)

func SetIOPriority(ioprio *configs.IOPriority) error {
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
iop := (class << 13) | ioprio.Priority
_, _, errno := syscall.RawSyscall(syscall.SYS_IOPRIO_SET, IoprioWhoPgrp, 0, uintptr(iop))
if errno != 0 {
return fmt.Errorf("failed to set io priority: %w", errno)
}
return nil
}
29 changes: 29 additions & 0 deletions tests/integration/ioprio.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/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 ]
echo "${output}" | grep -q "best-effort: prio 4"

# Check the process made from the exec command
runc exec test_ioprio ionice
[ "$status" -eq 0 ]
echo "${output}" | grep -q "best-effort: prio 4"
}
6 changes: 6 additions & 0 deletions utils_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ func newProcess(p specs.Process) (*libcontainer.Process, error) {
lp.ConsoleHeight = uint16(p.ConsoleSize.Height)
}

if p.IOPriority != nil {
lp.IOPriority = &configs.IOPriority{}
lp.IOPriority.Class = p.IOPriority.Class
lp.IOPriority.Priority = p.IOPriority.Priority
}

if p.Capabilities != nil {
lp.Capabilities = &configs.Capabilities{}
lp.Capabilities.Bounding = p.Capabilities.Bounding
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ github.com/moby/sys/mountinfo
# github.com/mrunalp/fileutils v0.5.0
## explicit; go 1.13
github.com/mrunalp/fileutils
# github.com/opencontainers/runtime-spec v1.1.0-rc.2
# github.com/opencontainers/runtime-spec v1.1.0-rc.3
## explicit
github.com/opencontainers/runtime-spec/specs-go
github.com/opencontainers/runtime-spec/specs-go/features
Expand Down

0 comments on commit 5994674

Please sign in to comment.