aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-11 20:03:08 +0900
committerOphestra <cat@gensokyo.uk>2026-03-11 20:03:08 +0900
commitfd515badffc159b62c5140b8340164ccd29e41f5 (patch)
treee4bfb24189960c360bd72e964d3f0acb3b28c4b7 /container
parent330a344845aa8653772a9be6c085f4fdb602a028 (diff)
container: move scheduler policy constants to std
This avoids depending on cgo. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/container.go4
-rw-r--r--container/std/syscall.go134
-rw-r--r--container/std/syscall_test.go92
-rw-r--r--container/syscall.go131
-rw-r--r--container/syscall_test.go100
5 files changed, 228 insertions, 233 deletions
diff --git a/container/container.go b/container/container.go
index e7fd8cee..49b3cfe6 100644
--- a/container/container.go
+++ b/container/container.go
@@ -40,7 +40,7 @@ type (
AllowOrphan bool
// Scheduling policy to set via sched_setscheduler(2). The zero value
// skips this call. Supported policies are [SCHED_BATCH], [SCHED_IDLE].
- SchedPolicy SchedPolicy
+ SchedPolicy std.SchedPolicy
// Cgroup fd, nil to disable.
Cgroup *int
// ExtraFiles passed through to initial process in the container, with
@@ -373,7 +373,7 @@ func (p *Container) Start() error {
// sched_setscheduler: thread-directed but acts on all processes
// created from the calling thread
- if p.SchedPolicy > 0 && p.SchedPolicy <= _SCHED_LAST {
+ if p.SchedPolicy > 0 && p.SchedPolicy <= std.SCHED_LAST {
var param schedParam
if priority, err := p.SchedPolicy.GetPriorityMin(); err != nil {
return &StartError{
diff --git a/container/std/syscall.go b/container/std/syscall.go
index eb71e071..8ebf33d4 100644
--- a/container/std/syscall.go
+++ b/container/std/syscall.go
@@ -1,6 +1,12 @@
package std
-import "iter"
+import (
+ "encoding"
+ "iter"
+ "strconv"
+ "sync"
+ "syscall"
+)
// Syscalls returns an iterator over all wired syscalls.
func Syscalls() iter.Seq2[string, ScmpSyscall] {
@@ -26,3 +32,129 @@ func SyscallResolveName(name string) (num ScmpSyscall, ok bool) {
num, ok = syscallNumExtra[name]
return
}
+
+// SchedPolicy denotes a scheduling policy defined in include/uapi/linux/sched.h.
+type SchedPolicy int
+
+// include/uapi/linux/sched.h
+const (
+ SCHED_NORMAL SchedPolicy = iota
+ SCHED_FIFO
+ SCHED_RR
+ SCHED_BATCH
+ _SCHED_ISO // SCHED_ISO: reserved but not implemented yet
+ SCHED_IDLE
+ SCHED_DEADLINE
+ SCHED_EXT
+
+ SCHED_LAST SchedPolicy = iota - 1
+)
+
+var _ encoding.TextMarshaler = SCHED_LAST
+var _ encoding.TextUnmarshaler = new(SCHED_LAST)
+
+// String returns a unique representation of policy, also used in encoding.
+func (policy SchedPolicy) String() string {
+ switch policy {
+ case SCHED_NORMAL:
+ return ""
+ case SCHED_FIFO:
+ return "fifo"
+ case SCHED_RR:
+ return "rr"
+ case SCHED_BATCH:
+ return "batch"
+ case SCHED_IDLE:
+ return "idle"
+ case SCHED_DEADLINE:
+ return "deadline"
+ case SCHED_EXT:
+ return "ext"
+
+ default:
+ return "invalid policy " + strconv.Itoa(int(policy))
+ }
+}
+
+// MarshalText performs bounds checking and returns the result of String.
+func (policy SchedPolicy) MarshalText() ([]byte, error) {
+ if policy == _SCHED_ISO || policy < 0 || policy > SCHED_LAST {
+ return nil, syscall.EINVAL
+ }
+ return []byte(policy.String()), nil
+}
+
+// InvalidSchedPolicyError is an invalid string representation of a [SchedPolicy].
+type InvalidSchedPolicyError string
+
+func (InvalidSchedPolicyError) Unwrap() error { return syscall.EINVAL }
+func (e InvalidSchedPolicyError) Error() string {
+ return "invalid scheduling policy " + strconv.Quote(string(e))
+}
+
+// UnmarshalText is the inverse of MarshalText.
+func (policy *SchedPolicy) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "fifo":
+ *policy = SCHED_FIFO
+ case "rr":
+ *policy = SCHED_RR
+ case "batch":
+ *policy = SCHED_BATCH
+ case "idle":
+ *policy = SCHED_IDLE
+ case "deadline":
+ *policy = SCHED_DEADLINE
+ case "ext":
+ *policy = SCHED_EXT
+
+ case "":
+ *policy = 0
+ return nil
+ default:
+ return InvalidSchedPolicyError(text)
+ }
+ return nil
+}
+
+// for sched_get_priority_max and sched_get_priority_min
+var (
+ schedPriority [SCHED_LAST + 1][2]Int
+ schedPriorityErr [SCHED_LAST + 1][2]error
+ schedPriorityOnce [SCHED_LAST + 1][2]sync.Once
+)
+
+// GetPriorityMax returns the maximum priority value that can be used with the
+// scheduling algorithm identified by policy.
+func (policy SchedPolicy) GetPriorityMax() (Int, error) {
+ schedPriorityOnce[policy][0].Do(func() {
+ priority, _, errno := syscall.Syscall(
+ syscall.SYS_SCHED_GET_PRIORITY_MAX,
+ uintptr(policy),
+ 0, 0,
+ )
+ schedPriority[policy][0] = Int(priority)
+ if schedPriority[policy][0] < 0 {
+ schedPriorityErr[policy][0] = errno
+ }
+ })
+ return schedPriority[policy][0], schedPriorityErr[policy][0]
+}
+
+// GetPriorityMin returns the minimum priority value that can be used with the
+// scheduling algorithm identified by policy.
+func (policy SchedPolicy) GetPriorityMin() (Int, error) {
+ schedPriorityOnce[policy][1].Do(func() {
+ priority, _, errno := syscall.Syscall(
+ syscall.SYS_SCHED_GET_PRIORITY_MIN,
+ uintptr(policy),
+ 0, 0,
+ )
+ schedPriority[policy][1] = Int(priority)
+ if schedPriority[policy][1] < 0 {
+ schedPriorityErr[policy][1] = errno
+ }
+ })
+ return schedPriority[policy][1], schedPriorityErr[policy][1]
+
+}
diff --git a/container/std/syscall_test.go b/container/std/syscall_test.go
index b82cc46a..3162b160 100644
--- a/container/std/syscall_test.go
+++ b/container/std/syscall_test.go
@@ -1,6 +1,11 @@
package std_test
import (
+ "encoding/json"
+ "errors"
+ "math"
+ "reflect"
+ "syscall"
"testing"
"hakurei.app/container/std"
@@ -19,3 +24,90 @@ func TestSyscallResolveName(t *testing.T) {
})
}
}
+
+func TestSchedPolicyJSON(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ policy std.SchedPolicy
+ want string
+ encodeErr error
+ decodeErr error
+ }{
+ {std.SCHED_NORMAL, `""`, nil, nil},
+ {std.SCHED_FIFO, `"fifo"`, nil, nil},
+ {std.SCHED_RR, `"rr"`, nil, nil},
+ {std.SCHED_BATCH, `"batch"`, nil, nil},
+ {4, `"invalid policy 4"`, syscall.EINVAL, std.InvalidSchedPolicyError("invalid policy 4")},
+ {std.SCHED_IDLE, `"idle"`, nil, nil},
+ {std.SCHED_DEADLINE, `"deadline"`, nil, nil},
+ {std.SCHED_EXT, `"ext"`, nil, nil},
+ {math.MaxInt, `"iso"`, syscall.EINVAL, std.InvalidSchedPolicyError("iso")},
+ }
+ for _, tc := range testCases {
+ name := tc.policy.String()
+ if tc.policy == std.SCHED_NORMAL {
+ name = "normal"
+ }
+
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := json.Marshal(tc.policy)
+ if !errors.Is(err, tc.encodeErr) {
+ t.Fatalf("Marshal: error = %v, want %v", err, tc.encodeErr)
+ }
+ if err == nil && string(got) != tc.want {
+ t.Fatalf("Marshal: %s, want %s", string(got), tc.want)
+ }
+
+ var v std.SchedPolicy
+ if err = json.Unmarshal([]byte(tc.want), &v); !reflect.DeepEqual(err, tc.decodeErr) {
+ t.Fatalf("Unmarshal: error = %v, want %v", err, tc.decodeErr)
+ }
+ if err == nil && v != tc.policy {
+ t.Fatalf("Unmarshal: %d, want %d", v, tc.policy)
+ }
+ })
+ }
+}
+
+func TestSchedPolicyMinMax(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ policy std.SchedPolicy
+ min, max std.Int
+ err error
+ }{
+ {std.SCHED_NORMAL, 0, 0, nil},
+ {std.SCHED_FIFO, 1, 99, nil},
+ {std.SCHED_RR, 1, 99, nil},
+ {std.SCHED_BATCH, 0, 0, nil},
+ {4, -1, -1, syscall.EINVAL},
+ {std.SCHED_IDLE, 0, 0, nil},
+ {std.SCHED_DEADLINE, 0, 0, nil},
+ {std.SCHED_EXT, 0, 0, nil},
+ }
+ for _, tc := range testCases {
+ name := tc.policy.String()
+ if tc.policy == std.SCHED_NORMAL {
+ name = "normal"
+ }
+
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ if priority, err := tc.policy.GetPriorityMax(); !reflect.DeepEqual(err, tc.err) {
+ t.Fatalf("GetPriorityMax: error = %v, want %v", err, tc.err)
+ } else if priority != tc.max {
+ t.Fatalf("GetPriorityMax: %d, want %d", priority, tc.max)
+ }
+ if priority, err := tc.policy.GetPriorityMin(); !reflect.DeepEqual(err, tc.err) {
+ t.Fatalf("GetPriorityMin: error = %v, want %v", err, tc.err)
+ } else if priority != tc.min {
+ t.Fatalf("GetPriorityMin: %d, want %d", priority, tc.min)
+ }
+ })
+ }
+}
diff --git a/container/syscall.go b/container/syscall.go
index 1b85b91a..cba9b7cc 100644
--- a/container/syscall.go
+++ b/container/syscall.go
@@ -1,9 +1,6 @@
package container
import (
- "encoding"
- "strconv"
- "sync"
. "syscall"
"unsafe"
@@ -46,132 +43,6 @@ func Isatty(fd int) bool {
return r == 0
}
-// SchedPolicy denotes a scheduling policy defined in include/uapi/linux/sched.h.
-type SchedPolicy int
-
-// include/uapi/linux/sched.h
-const (
- SCHED_NORMAL SchedPolicy = iota
- SCHED_FIFO
- SCHED_RR
- SCHED_BATCH
- _SCHED_ISO // SCHED_ISO: reserved but not implemented yet
- SCHED_IDLE
- SCHED_DEADLINE
- SCHED_EXT
-
- _SCHED_LAST SchedPolicy = iota - 1
-)
-
-var _ encoding.TextMarshaler = _SCHED_LAST
-var _ encoding.TextUnmarshaler = new(_SCHED_LAST)
-
-// String returns a unique representation of policy, also used in encoding.
-func (policy SchedPolicy) String() string {
- switch policy {
- case SCHED_NORMAL:
- return ""
- case SCHED_FIFO:
- return "fifo"
- case SCHED_RR:
- return "rr"
- case SCHED_BATCH:
- return "batch"
- case SCHED_IDLE:
- return "idle"
- case SCHED_DEADLINE:
- return "deadline"
- case SCHED_EXT:
- return "ext"
-
- default:
- return "invalid policy " + strconv.Itoa(int(policy))
- }
-}
-
-// MarshalText performs bounds checking and returns the result of String.
-func (policy SchedPolicy) MarshalText() ([]byte, error) {
- if policy == _SCHED_ISO || policy < 0 || policy > _SCHED_LAST {
- return nil, EINVAL
- }
- return []byte(policy.String()), nil
-}
-
-// InvalidSchedPolicyError is an invalid string representation of a [SchedPolicy].
-type InvalidSchedPolicyError string
-
-func (InvalidSchedPolicyError) Unwrap() error { return EINVAL }
-func (e InvalidSchedPolicyError) Error() string {
- return "invalid scheduling policy " + strconv.Quote(string(e))
-}
-
-// UnmarshalText is the inverse of MarshalText.
-func (policy *SchedPolicy) UnmarshalText(text []byte) error {
- switch string(text) {
- case "fifo":
- *policy = SCHED_FIFO
- case "rr":
- *policy = SCHED_RR
- case "batch":
- *policy = SCHED_BATCH
- case "idle":
- *policy = SCHED_IDLE
- case "deadline":
- *policy = SCHED_DEADLINE
- case "ext":
- *policy = SCHED_EXT
-
- case "":
- *policy = 0
- return nil
- default:
- return InvalidSchedPolicyError(text)
- }
- return nil
-}
-
-// for sched_get_priority_max and sched_get_priority_min
-var (
- schedPriority [_SCHED_LAST + 1][2]std.Int
- schedPriorityErr [_SCHED_LAST + 1][2]error
- schedPriorityOnce [_SCHED_LAST + 1][2]sync.Once
-)
-
-// GetPriorityMax returns the maximum priority value that can be used with the
-// scheduling algorithm identified by policy.
-func (policy SchedPolicy) GetPriorityMax() (std.Int, error) {
- schedPriorityOnce[policy][0].Do(func() {
- priority, _, errno := Syscall(
- SYS_SCHED_GET_PRIORITY_MAX,
- uintptr(policy),
- 0, 0,
- )
- schedPriority[policy][0] = std.Int(priority)
- if schedPriority[policy][0] < 0 {
- schedPriorityErr[policy][0] = errno
- }
- })
- return schedPriority[policy][0], schedPriorityErr[policy][0]
-}
-
-// GetPriorityMin returns the minimum priority value that can be used with the
-// scheduling algorithm identified by policy.
-func (policy SchedPolicy) GetPriorityMin() (std.Int, error) {
- schedPriorityOnce[policy][1].Do(func() {
- priority, _, errno := Syscall(
- SYS_SCHED_GET_PRIORITY_MIN,
- uintptr(policy),
- 0, 0,
- )
- schedPriority[policy][1] = std.Int(priority)
- if schedPriority[policy][1] < 0 {
- schedPriorityErr[policy][1] = errno
- }
- })
- return schedPriority[policy][1], schedPriorityErr[policy][1]
-
-}
-
// schedParam is equivalent to struct sched_param from include/linux/sched.h.
type schedParam struct {
// sched_priority
@@ -191,7 +62,7 @@ type schedParam struct {
// this if you do not have something similar in place!
//
// [very subtle to use correctly]: https://www.openwall.com/lists/musl/2016/03/01/4
-func schedSetscheduler(tid int, policy SchedPolicy, param *schedParam) error {
+func schedSetscheduler(tid int, policy std.SchedPolicy, param *schedParam) error {
if r, _, errno := Syscall(
SYS_SCHED_SETSCHEDULER,
uintptr(tid),
diff --git a/container/syscall_test.go b/container/syscall_test.go
deleted file mode 100644
index 337d6450..00000000
--- a/container/syscall_test.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package container_test
-
-import (
- "encoding/json"
- "errors"
- "math"
- "reflect"
- "syscall"
- "testing"
-
- "hakurei.app/container"
- "hakurei.app/container/std"
-)
-
-func TestSchedPolicyJSON(t *testing.T) {
- t.Parallel()
-
- testCases := []struct {
- policy container.SchedPolicy
- want string
- encodeErr error
- decodeErr error
- }{
- {container.SCHED_NORMAL, `""`, nil, nil},
- {container.SCHED_FIFO, `"fifo"`, nil, nil},
- {container.SCHED_RR, `"rr"`, nil, nil},
- {container.SCHED_BATCH, `"batch"`, nil, nil},
- {4, `"invalid policy 4"`, syscall.EINVAL, container.InvalidSchedPolicyError("invalid policy 4")},
- {container.SCHED_IDLE, `"idle"`, nil, nil},
- {container.SCHED_DEADLINE, `"deadline"`, nil, nil},
- {container.SCHED_EXT, `"ext"`, nil, nil},
- {math.MaxInt, `"iso"`, syscall.EINVAL, container.InvalidSchedPolicyError("iso")},
- }
- for _, tc := range testCases {
- name := tc.policy.String()
- if tc.policy == container.SCHED_NORMAL {
- name = "normal"
- }
-
- t.Run(name, func(t *testing.T) {
- t.Parallel()
-
- got, err := json.Marshal(tc.policy)
- if !errors.Is(err, tc.encodeErr) {
- t.Fatalf("Marshal: error = %v, want %v", err, tc.encodeErr)
- }
- if err == nil && string(got) != tc.want {
- t.Fatalf("Marshal: %s, want %s", string(got), tc.want)
- }
-
- var v container.SchedPolicy
- if err = json.Unmarshal([]byte(tc.want), &v); !reflect.DeepEqual(err, tc.decodeErr) {
- t.Fatalf("Unmarshal: error = %v, want %v", err, tc.decodeErr)
- }
- if err == nil && v != tc.policy {
- t.Fatalf("Unmarshal: %d, want %d", v, tc.policy)
- }
- })
- }
-}
-
-func TestSchedPolicyMinMax(t *testing.T) {
- t.Parallel()
-
- testCases := []struct {
- policy container.SchedPolicy
- min, max std.Int
- err error
- }{
- {container.SCHED_NORMAL, 0, 0, nil},
- {container.SCHED_FIFO, 1, 99, nil},
- {container.SCHED_RR, 1, 99, nil},
- {container.SCHED_BATCH, 0, 0, nil},
- {4, -1, -1, syscall.EINVAL},
- {container.SCHED_IDLE, 0, 0, nil},
- {container.SCHED_DEADLINE, 0, 0, nil},
- {container.SCHED_EXT, 0, 0, nil},
- }
- for _, tc := range testCases {
- name := tc.policy.String()
- if tc.policy == container.SCHED_NORMAL {
- name = "normal"
- }
-
- t.Run(name, func(t *testing.T) {
- t.Parallel()
-
- if priority, err := tc.policy.GetPriorityMax(); !reflect.DeepEqual(err, tc.err) {
- t.Fatalf("GetPriorityMax: error = %v, want %v", err, tc.err)
- } else if priority != tc.max {
- t.Fatalf("GetPriorityMax: %d, want %d", priority, tc.max)
- }
- if priority, err := tc.policy.GetPriorityMin(); !reflect.DeepEqual(err, tc.err) {
- t.Fatalf("GetPriorityMin: error = %v, want %v", err, tc.err)
- } else if priority != tc.min {
- t.Fatalf("GetPriorityMin: %d, want %d", priority, tc.min)
- }
- })
- }
-}