summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/kernel/semaphore
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/kernel/semaphore')
-rw-r--r--pkg/sentry/kernel/semaphore/BUILD50
-rw-r--r--pkg/sentry/kernel/semaphore/semaphore_state_autogen.go202
-rw-r--r--pkg/sentry/kernel/semaphore/semaphore_test.go174
-rw-r--r--pkg/sentry/kernel/semaphore/waiter_list.go221
4 files changed, 423 insertions, 224 deletions
diff --git a/pkg/sentry/kernel/semaphore/BUILD b/pkg/sentry/kernel/semaphore/BUILD
deleted file mode 100644
index 6aa74219e..000000000
--- a/pkg/sentry/kernel/semaphore/BUILD
+++ /dev/null
@@ -1,50 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-load("//tools/go_generics:defs.bzl", "go_template_instance")
-
-package(licenses = ["notice"])
-
-go_template_instance(
- name = "waiter_list",
- out = "waiter_list.go",
- package = "semaphore",
- prefix = "waiter",
- template = "//pkg/ilist:generic_list",
- types = {
- "Element": "*waiter",
- "Linker": "*waiter",
- },
-)
-
-go_library(
- name = "semaphore",
- srcs = [
- "semaphore.go",
- "waiter_list.go",
- ],
- visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/context",
- "//pkg/errors/linuxerr",
- "//pkg/sentry/fs",
- "//pkg/sentry/kernel/auth",
- "//pkg/sentry/kernel/ipc",
- "//pkg/sentry/kernel/time",
- "//pkg/sync",
- ],
-)
-
-go_test(
- name = "semaphore_test",
- size = "small",
- srcs = ["semaphore_test.go"],
- library = ":semaphore",
- deps = [
- "//pkg/abi/linux", # keep
- "//pkg/context", # keep
- "//pkg/errors/linuxerr", #keep
- "//pkg/sentry/contexttest", # keep
- "//pkg/sentry/kernel/auth", # keep
- "//pkg/sentry/kernel/ipc", # keep
- ],
-)
diff --git a/pkg/sentry/kernel/semaphore/semaphore_state_autogen.go b/pkg/sentry/kernel/semaphore/semaphore_state_autogen.go
new file mode 100644
index 000000000..7ea96b30d
--- /dev/null
+++ b/pkg/sentry/kernel/semaphore/semaphore_state_autogen.go
@@ -0,0 +1,202 @@
+// automatically generated by stateify.
+
+package semaphore
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (r *Registry) StateTypeName() string {
+ return "pkg/sentry/kernel/semaphore.Registry"
+}
+
+func (r *Registry) StateFields() []string {
+ return []string{
+ "reg",
+ "indexes",
+ }
+}
+
+func (r *Registry) beforeSave() {}
+
+// +checklocksignore
+func (r *Registry) StateSave(stateSinkObject state.Sink) {
+ r.beforeSave()
+ stateSinkObject.Save(0, &r.reg)
+ stateSinkObject.Save(1, &r.indexes)
+}
+
+func (r *Registry) afterLoad() {}
+
+// +checklocksignore
+func (r *Registry) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &r.reg)
+ stateSourceObject.Load(1, &r.indexes)
+}
+
+func (s *Set) StateTypeName() string {
+ return "pkg/sentry/kernel/semaphore.Set"
+}
+
+func (s *Set) StateFields() []string {
+ return []string{
+ "registry",
+ "obj",
+ "opTime",
+ "changeTime",
+ "sems",
+ "dead",
+ }
+}
+
+func (s *Set) beforeSave() {}
+
+// +checklocksignore
+func (s *Set) StateSave(stateSinkObject state.Sink) {
+ s.beforeSave()
+ stateSinkObject.Save(0, &s.registry)
+ stateSinkObject.Save(1, &s.obj)
+ stateSinkObject.Save(2, &s.opTime)
+ stateSinkObject.Save(3, &s.changeTime)
+ stateSinkObject.Save(4, &s.sems)
+ stateSinkObject.Save(5, &s.dead)
+}
+
+func (s *Set) afterLoad() {}
+
+// +checklocksignore
+func (s *Set) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &s.registry)
+ stateSourceObject.Load(1, &s.obj)
+ stateSourceObject.Load(2, &s.opTime)
+ stateSourceObject.Load(3, &s.changeTime)
+ stateSourceObject.Load(4, &s.sems)
+ stateSourceObject.Load(5, &s.dead)
+}
+
+func (s *sem) StateTypeName() string {
+ return "pkg/sentry/kernel/semaphore.sem"
+}
+
+func (s *sem) StateFields() []string {
+ return []string{
+ "value",
+ "pid",
+ }
+}
+
+func (s *sem) beforeSave() {}
+
+// +checklocksignore
+func (s *sem) StateSave(stateSinkObject state.Sink) {
+ s.beforeSave()
+ if !state.IsZeroValue(&s.waiters) {
+ state.Failf("waiters is %#v, expected zero", &s.waiters)
+ }
+ stateSinkObject.Save(0, &s.value)
+ stateSinkObject.Save(1, &s.pid)
+}
+
+func (s *sem) afterLoad() {}
+
+// +checklocksignore
+func (s *sem) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &s.value)
+ stateSourceObject.Load(1, &s.pid)
+}
+
+func (w *waiter) StateTypeName() string {
+ return "pkg/sentry/kernel/semaphore.waiter"
+}
+
+func (w *waiter) StateFields() []string {
+ return []string{
+ "waiterEntry",
+ "value",
+ "ch",
+ }
+}
+
+func (w *waiter) beforeSave() {}
+
+// +checklocksignore
+func (w *waiter) StateSave(stateSinkObject state.Sink) {
+ w.beforeSave()
+ stateSinkObject.Save(0, &w.waiterEntry)
+ stateSinkObject.Save(1, &w.value)
+ stateSinkObject.Save(2, &w.ch)
+}
+
+func (w *waiter) afterLoad() {}
+
+// +checklocksignore
+func (w *waiter) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &w.waiterEntry)
+ stateSourceObject.Load(1, &w.value)
+ stateSourceObject.Load(2, &w.ch)
+}
+
+func (l *waiterList) StateTypeName() string {
+ return "pkg/sentry/kernel/semaphore.waiterList"
+}
+
+func (l *waiterList) StateFields() []string {
+ return []string{
+ "head",
+ "tail",
+ }
+}
+
+func (l *waiterList) beforeSave() {}
+
+// +checklocksignore
+func (l *waiterList) StateSave(stateSinkObject state.Sink) {
+ l.beforeSave()
+ stateSinkObject.Save(0, &l.head)
+ stateSinkObject.Save(1, &l.tail)
+}
+
+func (l *waiterList) afterLoad() {}
+
+// +checklocksignore
+func (l *waiterList) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &l.head)
+ stateSourceObject.Load(1, &l.tail)
+}
+
+func (e *waiterEntry) StateTypeName() string {
+ return "pkg/sentry/kernel/semaphore.waiterEntry"
+}
+
+func (e *waiterEntry) StateFields() []string {
+ return []string{
+ "next",
+ "prev",
+ }
+}
+
+func (e *waiterEntry) beforeSave() {}
+
+// +checklocksignore
+func (e *waiterEntry) StateSave(stateSinkObject state.Sink) {
+ e.beforeSave()
+ stateSinkObject.Save(0, &e.next)
+ stateSinkObject.Save(1, &e.prev)
+}
+
+func (e *waiterEntry) afterLoad() {}
+
+// +checklocksignore
+func (e *waiterEntry) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &e.next)
+ stateSourceObject.Load(1, &e.prev)
+}
+
+func init() {
+ state.Register((*Registry)(nil))
+ state.Register((*Set)(nil))
+ state.Register((*sem)(nil))
+ state.Register((*waiter)(nil))
+ state.Register((*waiterList)(nil))
+ state.Register((*waiterEntry)(nil))
+}
diff --git a/pkg/sentry/kernel/semaphore/semaphore_test.go b/pkg/sentry/kernel/semaphore/semaphore_test.go
deleted file mode 100644
index 59ac92ef1..000000000
--- a/pkg/sentry/kernel/semaphore/semaphore_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package semaphore
-
-import (
- "testing"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/errors/linuxerr"
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
- "gvisor.dev/gvisor/pkg/sentry/kernel/ipc"
-)
-
-func executeOps(ctx context.Context, t *testing.T, set *Set, ops []linux.Sembuf, block bool) chan struct{} {
- ch, _, err := set.executeOps(ctx, ops, 123)
- if err != nil {
- t.Fatalf("ExecuteOps(ops) failed, err: %v, ops: %+v", err, ops)
- }
- if block {
- if ch == nil {
- t.Fatalf("ExecuteOps(ops) got: nil, expected: !nil, ops: %+v", ops)
- }
- if signalled(ch) {
- t.Fatalf("ExecuteOps(ops) channel should not have been signalled, ops: %+v", ops)
- }
- } else {
- if ch != nil {
- t.Fatalf("ExecuteOps(ops) got: %v, expected: nil, ops: %+v", ch, ops)
- }
- }
- return ch
-}
-
-func signalled(ch chan struct{}) bool {
- select {
- case <-ch:
- return true
- default:
- return false
- }
-}
-
-func TestBasic(t *testing.T) {
- ctx := contexttest.Context(t)
- set := &Set{obj: &ipc.Object{ID: 123}, sems: make([]sem, 1)}
- ops := []linux.Sembuf{
- {SemOp: 1},
- }
- executeOps(ctx, t, set, ops, false)
-
- ops[0].SemOp = -1
- executeOps(ctx, t, set, ops, false)
-
- ops[0].SemOp = -1
- ch1 := executeOps(ctx, t, set, ops, true)
-
- ops[0].SemOp = 1
- executeOps(ctx, t, set, ops, false)
- if !signalled(ch1) {
- t.Fatalf("ExecuteOps(ops) channel should not have been signalled, ops: %+v", ops)
- }
-}
-
-func TestWaitForZero(t *testing.T) {
- ctx := contexttest.Context(t)
- set := &Set{obj: &ipc.Object{ID: 123}, sems: make([]sem, 1)}
- ops := []linux.Sembuf{
- {SemOp: 0},
- }
- executeOps(ctx, t, set, ops, false)
-
- ops[0].SemOp = -2
- ch1 := executeOps(ctx, t, set, ops, true)
-
- ops[0].SemOp = 0
- executeOps(ctx, t, set, ops, false)
-
- ops[0].SemOp = 1
- executeOps(ctx, t, set, ops, false)
-
- ops[0].SemOp = 0
- chZero1 := executeOps(ctx, t, set, ops, true)
-
- ops[0].SemOp = 0
- chZero2 := executeOps(ctx, t, set, ops, true)
-
- ops[0].SemOp = 1
- executeOps(ctx, t, set, ops, false)
- if !signalled(ch1) {
- t.Fatalf("ExecuteOps(ops) channel should have been signalled, ops: %+v, set: %+v", ops, set)
- }
-
- ops[0].SemOp = -2
- executeOps(ctx, t, set, ops, false)
- if !signalled(chZero1) {
- t.Fatalf("ExecuteOps(ops) channel zero 1 should have been signalled, ops: %+v, set: %+v", ops, set)
- }
- if !signalled(chZero2) {
- t.Fatalf("ExecuteOps(ops) channel zero 2 should have been signalled, ops: %+v, set: %+v", ops, set)
- }
-}
-
-func TestNoWait(t *testing.T) {
- ctx := contexttest.Context(t)
- set := &Set{obj: &ipc.Object{ID: 123}, sems: make([]sem, 1)}
- ops := []linux.Sembuf{
- {SemOp: 1},
- }
- executeOps(ctx, t, set, ops, false)
-
- ops[0].SemOp = -2
- ops[0].SemFlg = linux.IPC_NOWAIT
- if _, _, err := set.executeOps(ctx, ops, 123); err != linuxerr.ErrWouldBlock {
- t.Fatalf("ExecuteOps(ops) wrong result, got: %v, expected: %v", err, linuxerr.ErrWouldBlock)
- }
-
- ops[0].SemOp = 0
- ops[0].SemFlg = linux.IPC_NOWAIT
- if _, _, err := set.executeOps(ctx, ops, 123); err != linuxerr.ErrWouldBlock {
- t.Fatalf("ExecuteOps(ops) wrong result, got: %v, expected: %v", err, linuxerr.ErrWouldBlock)
- }
-}
-
-func TestUnregister(t *testing.T) {
- ctx := contexttest.Context(t)
- r := NewRegistry(auth.NewRootUserNamespace())
- set, err := r.FindOrCreate(ctx, 123, 2, linux.FileMode(0x600), true, true, true)
-
- if err != nil {
- t.Fatalf("FindOrCreate() failed, err: %v", err)
- }
- if got := r.FindByID(set.obj.ID); got.obj.ID != set.obj.ID {
- t.Fatalf("FindById(%d) failed, got: %+v, expected: %+v", set.obj.ID, got, set)
- }
-
- ops := []linux.Sembuf{
- {SemOp: -1},
- }
- chs := make([]chan struct{}, 0, 5)
- for i := 0; i < 5; i++ {
- ch := executeOps(ctx, t, set, ops, true)
- chs = append(chs, ch)
- }
-
- creds := auth.CredentialsFromContext(ctx)
- if err := r.Remove(set.obj.ID, creds); err != nil {
- t.Fatalf("Remove(%d) failed, err: %v", set.obj.ID, err)
- }
- if !set.dead {
- t.Fatalf("set is not dead: %+v", set)
- }
- if got := r.FindByID(set.obj.ID); got != nil {
- t.Fatalf("FindById(%d) failed, got: %+v, expected: nil", set.obj.ID, got)
- }
- for i, ch := range chs {
- if !signalled(ch) {
- t.Fatalf("channel %d should have been signalled", i)
- }
- }
-}
diff --git a/pkg/sentry/kernel/semaphore/waiter_list.go b/pkg/sentry/kernel/semaphore/waiter_list.go
new file mode 100644
index 000000000..51586d43f
--- /dev/null
+++ b/pkg/sentry/kernel/semaphore/waiter_list.go
@@ -0,0 +1,221 @@
+package semaphore
+
+// ElementMapper provides an identity mapping by default.
+//
+// This can be replaced to provide a struct that maps elements to linker
+// objects, if they are not the same. An ElementMapper is not typically
+// required if: Linker is left as is, Element is left as is, or Linker and
+// Element are the same type.
+type waiterElementMapper struct{}
+
+// linkerFor maps an Element to a Linker.
+//
+// This default implementation should be inlined.
+//
+//go:nosplit
+func (waiterElementMapper) linkerFor(elem *waiter) *waiter { return elem }
+
+// List is an intrusive list. Entries can be added to or removed from the list
+// in O(1) time and with no additional memory allocations.
+//
+// The zero value for List is an empty list ready to use.
+//
+// To iterate over a list (where l is a List):
+// for e := l.Front(); e != nil; e = e.Next() {
+// // do something with e.
+// }
+//
+// +stateify savable
+type waiterList struct {
+ head *waiter
+ tail *waiter
+}
+
+// Reset resets list l to the empty state.
+func (l *waiterList) Reset() {
+ l.head = nil
+ l.tail = nil
+}
+
+// Empty returns true iff the list is empty.
+//
+//go:nosplit
+func (l *waiterList) Empty() bool {
+ return l.head == nil
+}
+
+// Front returns the first element of list l or nil.
+//
+//go:nosplit
+func (l *waiterList) Front() *waiter {
+ return l.head
+}
+
+// Back returns the last element of list l or nil.
+//
+//go:nosplit
+func (l *waiterList) Back() *waiter {
+ return l.tail
+}
+
+// Len returns the number of elements in the list.
+//
+// NOTE: This is an O(n) operation.
+//
+//go:nosplit
+func (l *waiterList) Len() (count int) {
+ for e := l.Front(); e != nil; e = (waiterElementMapper{}.linkerFor(e)).Next() {
+ count++
+ }
+ return count
+}
+
+// PushFront inserts the element e at the front of list l.
+//
+//go:nosplit
+func (l *waiterList) PushFront(e *waiter) {
+ linker := waiterElementMapper{}.linkerFor(e)
+ linker.SetNext(l.head)
+ linker.SetPrev(nil)
+ if l.head != nil {
+ waiterElementMapper{}.linkerFor(l.head).SetPrev(e)
+ } else {
+ l.tail = e
+ }
+
+ l.head = e
+}
+
+// PushBack inserts the element e at the back of list l.
+//
+//go:nosplit
+func (l *waiterList) PushBack(e *waiter) {
+ linker := waiterElementMapper{}.linkerFor(e)
+ linker.SetNext(nil)
+ linker.SetPrev(l.tail)
+ if l.tail != nil {
+ waiterElementMapper{}.linkerFor(l.tail).SetNext(e)
+ } else {
+ l.head = e
+ }
+
+ l.tail = e
+}
+
+// PushBackList inserts list m at the end of list l, emptying m.
+//
+//go:nosplit
+func (l *waiterList) PushBackList(m *waiterList) {
+ if l.head == nil {
+ l.head = m.head
+ l.tail = m.tail
+ } else if m.head != nil {
+ waiterElementMapper{}.linkerFor(l.tail).SetNext(m.head)
+ waiterElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
+
+ l.tail = m.tail
+ }
+ m.head = nil
+ m.tail = nil
+}
+
+// InsertAfter inserts e after b.
+//
+//go:nosplit
+func (l *waiterList) InsertAfter(b, e *waiter) {
+ bLinker := waiterElementMapper{}.linkerFor(b)
+ eLinker := waiterElementMapper{}.linkerFor(e)
+
+ a := bLinker.Next()
+
+ eLinker.SetNext(a)
+ eLinker.SetPrev(b)
+ bLinker.SetNext(e)
+
+ if a != nil {
+ waiterElementMapper{}.linkerFor(a).SetPrev(e)
+ } else {
+ l.tail = e
+ }
+}
+
+// InsertBefore inserts e before a.
+//
+//go:nosplit
+func (l *waiterList) InsertBefore(a, e *waiter) {
+ aLinker := waiterElementMapper{}.linkerFor(a)
+ eLinker := waiterElementMapper{}.linkerFor(e)
+
+ b := aLinker.Prev()
+ eLinker.SetNext(a)
+ eLinker.SetPrev(b)
+ aLinker.SetPrev(e)
+
+ if b != nil {
+ waiterElementMapper{}.linkerFor(b).SetNext(e)
+ } else {
+ l.head = e
+ }
+}
+
+// Remove removes e from l.
+//
+//go:nosplit
+func (l *waiterList) Remove(e *waiter) {
+ linker := waiterElementMapper{}.linkerFor(e)
+ prev := linker.Prev()
+ next := linker.Next()
+
+ if prev != nil {
+ waiterElementMapper{}.linkerFor(prev).SetNext(next)
+ } else if l.head == e {
+ l.head = next
+ }
+
+ if next != nil {
+ waiterElementMapper{}.linkerFor(next).SetPrev(prev)
+ } else if l.tail == e {
+ l.tail = prev
+ }
+
+ linker.SetNext(nil)
+ linker.SetPrev(nil)
+}
+
+// Entry is a default implementation of Linker. Users can add anonymous fields
+// of this type to their structs to make them automatically implement the
+// methods needed by List.
+//
+// +stateify savable
+type waiterEntry struct {
+ next *waiter
+ prev *waiter
+}
+
+// Next returns the entry that follows e in the list.
+//
+//go:nosplit
+func (e *waiterEntry) Next() *waiter {
+ return e.next
+}
+
+// Prev returns the entry that precedes e in the list.
+//
+//go:nosplit
+func (e *waiterEntry) Prev() *waiter {
+ return e.prev
+}
+
+// SetNext assigns 'entry' as the entry that follows e in the list.
+//
+//go:nosplit
+func (e *waiterEntry) SetNext(elem *waiter) {
+ e.next = elem
+}
+
+// SetPrev assigns 'entry' as the entry that precedes e in the list.
+//
+//go:nosplit
+func (e *waiterEntry) SetPrev(elem *waiter) {
+ e.prev = elem
+}