summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/kernel/epoll
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/kernel/epoll')
-rw-r--r--pkg/sentry/kernel/epoll/BUILD51
-rw-r--r--pkg/sentry/kernel/epoll/epoll_list.go221
-rw-r--r--pkg/sentry/kernel/epoll/epoll_state_autogen.go182
-rw-r--r--pkg/sentry/kernel/epoll/epoll_test.go55
4 files changed, 403 insertions, 106 deletions
diff --git a/pkg/sentry/kernel/epoll/BUILD b/pkg/sentry/kernel/epoll/BUILD
deleted file mode 100644
index 75eedd5a2..000000000
--- a/pkg/sentry/kernel/epoll/BUILD
+++ /dev/null
@@ -1,51 +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 = "epoll_list",
- out = "epoll_list.go",
- package = "epoll",
- prefix = "pollEntry",
- template = "//pkg/ilist:generic_list",
- types = {
- "Element": "*pollEntry",
- "Linker": "*pollEntry",
- },
-)
-
-go_library(
- name = "epoll",
- srcs = [
- "epoll.go",
- "epoll_list.go",
- "epoll_state.go",
- ],
- visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/context",
- "//pkg/refs",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/anon",
- "//pkg/sentry/fs/fsutil",
- "//pkg/sync",
- "//pkg/usermem",
- "//pkg/waiter",
- ],
-)
-
-go_test(
- name = "epoll_test",
- size = "small",
- srcs = [
- "epoll_test.go",
- ],
- library = ":epoll",
- deps = [
- "//pkg/sentry/contexttest",
- "//pkg/sentry/fs/filetest",
- "//pkg/waiter",
- ],
-)
diff --git a/pkg/sentry/kernel/epoll/epoll_list.go b/pkg/sentry/kernel/epoll/epoll_list.go
new file mode 100644
index 000000000..b6abe2de9
--- /dev/null
+++ b/pkg/sentry/kernel/epoll/epoll_list.go
@@ -0,0 +1,221 @@
+package epoll
+
+// 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 pollEntryElementMapper struct{}
+
+// linkerFor maps an Element to a Linker.
+//
+// This default implementation should be inlined.
+//
+//go:nosplit
+func (pollEntryElementMapper) linkerFor(elem *pollEntry) *pollEntry { 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 pollEntryList struct {
+ head *pollEntry
+ tail *pollEntry
+}
+
+// Reset resets list l to the empty state.
+func (l *pollEntryList) Reset() {
+ l.head = nil
+ l.tail = nil
+}
+
+// Empty returns true iff the list is empty.
+//
+//go:nosplit
+func (l *pollEntryList) Empty() bool {
+ return l.head == nil
+}
+
+// Front returns the first element of list l or nil.
+//
+//go:nosplit
+func (l *pollEntryList) Front() *pollEntry {
+ return l.head
+}
+
+// Back returns the last element of list l or nil.
+//
+//go:nosplit
+func (l *pollEntryList) Back() *pollEntry {
+ return l.tail
+}
+
+// Len returns the number of elements in the list.
+//
+// NOTE: This is an O(n) operation.
+//
+//go:nosplit
+func (l *pollEntryList) Len() (count int) {
+ for e := l.Front(); e != nil; e = (pollEntryElementMapper{}.linkerFor(e)).Next() {
+ count++
+ }
+ return count
+}
+
+// PushFront inserts the element e at the front of list l.
+//
+//go:nosplit
+func (l *pollEntryList) PushFront(e *pollEntry) {
+ linker := pollEntryElementMapper{}.linkerFor(e)
+ linker.SetNext(l.head)
+ linker.SetPrev(nil)
+ if l.head != nil {
+ pollEntryElementMapper{}.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 *pollEntryList) PushBack(e *pollEntry) {
+ linker := pollEntryElementMapper{}.linkerFor(e)
+ linker.SetNext(nil)
+ linker.SetPrev(l.tail)
+ if l.tail != nil {
+ pollEntryElementMapper{}.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 *pollEntryList) PushBackList(m *pollEntryList) {
+ if l.head == nil {
+ l.head = m.head
+ l.tail = m.tail
+ } else if m.head != nil {
+ pollEntryElementMapper{}.linkerFor(l.tail).SetNext(m.head)
+ pollEntryElementMapper{}.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 *pollEntryList) InsertAfter(b, e *pollEntry) {
+ bLinker := pollEntryElementMapper{}.linkerFor(b)
+ eLinker := pollEntryElementMapper{}.linkerFor(e)
+
+ a := bLinker.Next()
+
+ eLinker.SetNext(a)
+ eLinker.SetPrev(b)
+ bLinker.SetNext(e)
+
+ if a != nil {
+ pollEntryElementMapper{}.linkerFor(a).SetPrev(e)
+ } else {
+ l.tail = e
+ }
+}
+
+// InsertBefore inserts e before a.
+//
+//go:nosplit
+func (l *pollEntryList) InsertBefore(a, e *pollEntry) {
+ aLinker := pollEntryElementMapper{}.linkerFor(a)
+ eLinker := pollEntryElementMapper{}.linkerFor(e)
+
+ b := aLinker.Prev()
+ eLinker.SetNext(a)
+ eLinker.SetPrev(b)
+ aLinker.SetPrev(e)
+
+ if b != nil {
+ pollEntryElementMapper{}.linkerFor(b).SetNext(e)
+ } else {
+ l.head = e
+ }
+}
+
+// Remove removes e from l.
+//
+//go:nosplit
+func (l *pollEntryList) Remove(e *pollEntry) {
+ linker := pollEntryElementMapper{}.linkerFor(e)
+ prev := linker.Prev()
+ next := linker.Next()
+
+ if prev != nil {
+ pollEntryElementMapper{}.linkerFor(prev).SetNext(next)
+ } else if l.head == e {
+ l.head = next
+ }
+
+ if next != nil {
+ pollEntryElementMapper{}.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 pollEntryEntry struct {
+ next *pollEntry
+ prev *pollEntry
+}
+
+// Next returns the entry that follows e in the list.
+//
+//go:nosplit
+func (e *pollEntryEntry) Next() *pollEntry {
+ return e.next
+}
+
+// Prev returns the entry that precedes e in the list.
+//
+//go:nosplit
+func (e *pollEntryEntry) Prev() *pollEntry {
+ return e.prev
+}
+
+// SetNext assigns 'entry' as the entry that follows e in the list.
+//
+//go:nosplit
+func (e *pollEntryEntry) SetNext(elem *pollEntry) {
+ e.next = elem
+}
+
+// SetPrev assigns 'entry' as the entry that precedes e in the list.
+//
+//go:nosplit
+func (e *pollEntryEntry) SetPrev(elem *pollEntry) {
+ e.prev = elem
+}
diff --git a/pkg/sentry/kernel/epoll/epoll_state_autogen.go b/pkg/sentry/kernel/epoll/epoll_state_autogen.go
new file mode 100644
index 000000000..ba00fe082
--- /dev/null
+++ b/pkg/sentry/kernel/epoll/epoll_state_autogen.go
@@ -0,0 +1,182 @@
+// automatically generated by stateify.
+
+package epoll
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (f *FileIdentifier) StateTypeName() string {
+ return "pkg/sentry/kernel/epoll.FileIdentifier"
+}
+
+func (f *FileIdentifier) StateFields() []string {
+ return []string{
+ "File",
+ "Fd",
+ }
+}
+
+func (f *FileIdentifier) beforeSave() {}
+
+func (f *FileIdentifier) StateSave(stateSinkObject state.Sink) {
+ f.beforeSave()
+ stateSinkObject.Save(0, &f.File)
+ stateSinkObject.Save(1, &f.Fd)
+}
+
+func (f *FileIdentifier) afterLoad() {}
+
+func (f *FileIdentifier) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.LoadWait(0, &f.File)
+ stateSourceObject.Load(1, &f.Fd)
+}
+
+func (p *pollEntry) StateTypeName() string {
+ return "pkg/sentry/kernel/epoll.pollEntry"
+}
+
+func (p *pollEntry) StateFields() []string {
+ return []string{
+ "pollEntryEntry",
+ "id",
+ "userData",
+ "mask",
+ "flags",
+ "epoll",
+ }
+}
+
+func (p *pollEntry) beforeSave() {}
+
+func (p *pollEntry) StateSave(stateSinkObject state.Sink) {
+ p.beforeSave()
+ stateSinkObject.Save(0, &p.pollEntryEntry)
+ stateSinkObject.Save(1, &p.id)
+ stateSinkObject.Save(2, &p.userData)
+ stateSinkObject.Save(3, &p.mask)
+ stateSinkObject.Save(4, &p.flags)
+ stateSinkObject.Save(5, &p.epoll)
+}
+
+func (p *pollEntry) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &p.pollEntryEntry)
+ stateSourceObject.LoadWait(1, &p.id)
+ stateSourceObject.Load(2, &p.userData)
+ stateSourceObject.Load(3, &p.mask)
+ stateSourceObject.Load(4, &p.flags)
+ stateSourceObject.Load(5, &p.epoll)
+ stateSourceObject.AfterLoad(p.afterLoad)
+}
+
+func (e *EventPoll) StateTypeName() string {
+ return "pkg/sentry/kernel/epoll.EventPoll"
+}
+
+func (e *EventPoll) StateFields() []string {
+ return []string{
+ "files",
+ "readyList",
+ "waitingList",
+ "disabledList",
+ }
+}
+
+func (e *EventPoll) beforeSave() {}
+
+func (e *EventPoll) StateSave(stateSinkObject state.Sink) {
+ e.beforeSave()
+ if !state.IsZeroValue(&e.FilePipeSeek) {
+ state.Failf("FilePipeSeek is %#v, expected zero", &e.FilePipeSeek)
+ }
+ if !state.IsZeroValue(&e.FileNotDirReaddir) {
+ state.Failf("FileNotDirReaddir is %#v, expected zero", &e.FileNotDirReaddir)
+ }
+ if !state.IsZeroValue(&e.FileNoFsync) {
+ state.Failf("FileNoFsync is %#v, expected zero", &e.FileNoFsync)
+ }
+ if !state.IsZeroValue(&e.FileNoopFlush) {
+ state.Failf("FileNoopFlush is %#v, expected zero", &e.FileNoopFlush)
+ }
+ if !state.IsZeroValue(&e.FileNoIoctl) {
+ state.Failf("FileNoIoctl is %#v, expected zero", &e.FileNoIoctl)
+ }
+ if !state.IsZeroValue(&e.FileNoMMap) {
+ state.Failf("FileNoMMap is %#v, expected zero", &e.FileNoMMap)
+ }
+ if !state.IsZeroValue(&e.Queue) {
+ state.Failf("Queue is %#v, expected zero", &e.Queue)
+ }
+ stateSinkObject.Save(0, &e.files)
+ stateSinkObject.Save(1, &e.readyList)
+ stateSinkObject.Save(2, &e.waitingList)
+ stateSinkObject.Save(3, &e.disabledList)
+}
+
+func (e *EventPoll) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &e.files)
+ stateSourceObject.Load(1, &e.readyList)
+ stateSourceObject.Load(2, &e.waitingList)
+ stateSourceObject.Load(3, &e.disabledList)
+ stateSourceObject.AfterLoad(e.afterLoad)
+}
+
+func (l *pollEntryList) StateTypeName() string {
+ return "pkg/sentry/kernel/epoll.pollEntryList"
+}
+
+func (l *pollEntryList) StateFields() []string {
+ return []string{
+ "head",
+ "tail",
+ }
+}
+
+func (l *pollEntryList) beforeSave() {}
+
+func (l *pollEntryList) StateSave(stateSinkObject state.Sink) {
+ l.beforeSave()
+ stateSinkObject.Save(0, &l.head)
+ stateSinkObject.Save(1, &l.tail)
+}
+
+func (l *pollEntryList) afterLoad() {}
+
+func (l *pollEntryList) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &l.head)
+ stateSourceObject.Load(1, &l.tail)
+}
+
+func (e *pollEntryEntry) StateTypeName() string {
+ return "pkg/sentry/kernel/epoll.pollEntryEntry"
+}
+
+func (e *pollEntryEntry) StateFields() []string {
+ return []string{
+ "next",
+ "prev",
+ }
+}
+
+func (e *pollEntryEntry) beforeSave() {}
+
+func (e *pollEntryEntry) StateSave(stateSinkObject state.Sink) {
+ e.beforeSave()
+ stateSinkObject.Save(0, &e.next)
+ stateSinkObject.Save(1, &e.prev)
+}
+
+func (e *pollEntryEntry) afterLoad() {}
+
+func (e *pollEntryEntry) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &e.next)
+ stateSourceObject.Load(1, &e.prev)
+}
+
+func init() {
+ state.Register((*FileIdentifier)(nil))
+ state.Register((*pollEntry)(nil))
+ state.Register((*EventPoll)(nil))
+ state.Register((*pollEntryList)(nil))
+ state.Register((*pollEntryEntry)(nil))
+}
diff --git a/pkg/sentry/kernel/epoll/epoll_test.go b/pkg/sentry/kernel/epoll/epoll_test.go
deleted file mode 100644
index 55b505593..000000000
--- a/pkg/sentry/kernel/epoll/epoll_test.go
+++ /dev/null
@@ -1,55 +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 epoll
-
-import (
- "testing"
-
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs/filetest"
- "gvisor.dev/gvisor/pkg/waiter"
-)
-
-func TestFileDestroyed(t *testing.T) {
- f := filetest.NewTestFile(t)
- id := FileIdentifier{f, 12}
-
- ctx := contexttest.Context(t)
- efile := NewEventPoll(ctx)
- e := efile.FileOperations.(*EventPoll)
- if err := e.AddEntry(id, 0, waiter.EventIn, [2]int32{}); err != nil {
- t.Fatalf("addEntry failed: %v", err)
- }
-
- // Check that we get an event reported twice in a row.
- evt := e.ReadEvents(1)
- if len(evt) != 1 {
- t.Fatalf("Unexpected number of ready events: want %v, got %v", 1, len(evt))
- }
-
- evt = e.ReadEvents(1)
- if len(evt) != 1 {
- t.Fatalf("Unexpected number of ready events: want %v, got %v", 1, len(evt))
- }
-
- // Destroy the file. Check that we get no more events.
- f.DecRef(ctx)
-
- evt = e.ReadEvents(1)
- if len(evt) != 0 {
- t.Fatalf("Unexpected number of ready events: want %v, got %v", 0, len(evt))
- }
-
-}