github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/epoll.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package syscalls 16 17 import ( 18 "time" 19 20 "github.com/SagerNet/gvisor/pkg/abi/linux" 21 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 22 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 23 "github.com/SagerNet/gvisor/pkg/sentry/kernel/epoll" 24 ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 25 "github.com/SagerNet/gvisor/pkg/waiter" 26 ) 27 28 // CreateEpoll implements the epoll_create(2) linux syscall. 29 func CreateEpoll(t *kernel.Task, closeOnExec bool) (int32, error) { 30 file := epoll.NewEventPoll(t) 31 defer file.DecRef(t) 32 33 fd, err := t.NewFDFrom(0, file, kernel.FDFlags{ 34 CloseOnExec: closeOnExec, 35 }) 36 if err != nil { 37 return 0, err 38 } 39 40 return fd, nil 41 } 42 43 // AddEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_ADD. 44 func AddEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask waiter.EventMask, userData [2]int32) error { 45 // Get epoll from the file descriptor. 46 epollfile := t.GetFile(epfd) 47 if epollfile == nil { 48 return linuxerr.EBADF 49 } 50 defer epollfile.DecRef(t) 51 52 // Get the target file id. 53 file := t.GetFile(fd) 54 if file == nil { 55 return linuxerr.EBADF 56 } 57 defer file.DecRef(t) 58 59 // Extract the epollPoll operations. 60 e, ok := epollfile.FileOperations.(*epoll.EventPoll) 61 if !ok { 62 return linuxerr.EBADF 63 } 64 65 // Try to add the entry. 66 return e.AddEntry(epoll.FileIdentifier{file, fd}, flags, mask, userData) 67 } 68 69 // UpdateEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_MOD. 70 func UpdateEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask waiter.EventMask, userData [2]int32) error { 71 // Get epoll from the file descriptor. 72 epollfile := t.GetFile(epfd) 73 if epollfile == nil { 74 return linuxerr.EBADF 75 } 76 defer epollfile.DecRef(t) 77 78 // Get the target file id. 79 file := t.GetFile(fd) 80 if file == nil { 81 return linuxerr.EBADF 82 } 83 defer file.DecRef(t) 84 85 // Extract the epollPoll operations. 86 e, ok := epollfile.FileOperations.(*epoll.EventPoll) 87 if !ok { 88 return linuxerr.EBADF 89 } 90 91 // Try to update the entry. 92 return e.UpdateEntry(epoll.FileIdentifier{file, fd}, flags, mask, userData) 93 } 94 95 // RemoveEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_DEL. 96 func RemoveEpoll(t *kernel.Task, epfd int32, fd int32) error { 97 // Get epoll from the file descriptor. 98 epollfile := t.GetFile(epfd) 99 if epollfile == nil { 100 return linuxerr.EBADF 101 } 102 defer epollfile.DecRef(t) 103 104 // Get the target file id. 105 file := t.GetFile(fd) 106 if file == nil { 107 return linuxerr.EBADF 108 } 109 defer file.DecRef(t) 110 111 // Extract the epollPoll operations. 112 e, ok := epollfile.FileOperations.(*epoll.EventPoll) 113 if !ok { 114 return linuxerr.EBADF 115 } 116 117 // Try to remove the entry. 118 return e.RemoveEntry(t, epoll.FileIdentifier{file, fd}) 119 } 120 121 // WaitEpoll implements the epoll_wait(2) linux syscall. 122 func WaitEpoll(t *kernel.Task, fd int32, max int, timeoutInNanos int64) ([]linux.EpollEvent, error) { 123 // Get epoll from the file descriptor. 124 epollfile := t.GetFile(fd) 125 if epollfile == nil { 126 return nil, linuxerr.EBADF 127 } 128 defer epollfile.DecRef(t) 129 130 // Extract the epollPoll operations. 131 e, ok := epollfile.FileOperations.(*epoll.EventPoll) 132 if !ok { 133 return nil, linuxerr.EBADF 134 } 135 136 // Try to read events and return right away if we got them or if the 137 // caller requested a non-blocking "wait". 138 r := e.ReadEvents(max) 139 if len(r) != 0 || timeoutInNanos == 0 { 140 return r, nil 141 } 142 143 // We'll have to wait. Set up the timer if a timeout was specified and 144 // and register with the epoll object for readability events. 145 var haveDeadline bool 146 var deadline ktime.Time 147 if timeoutInNanos > 0 { 148 timeoutDur := time.Duration(timeoutInNanos) * time.Nanosecond 149 deadline = t.Kernel().MonotonicClock().Now().Add(timeoutDur) 150 haveDeadline = true 151 } 152 153 w, ch := waiter.NewChannelEntry(nil) 154 e.EventRegister(&w, waiter.ReadableEvents) 155 defer e.EventUnregister(&w) 156 157 // Try to read the events again until we succeed, timeout or get 158 // interrupted. 159 for { 160 r = e.ReadEvents(max) 161 if len(r) != 0 { 162 return r, nil 163 } 164 165 if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil { 166 if linuxerr.Equals(linuxerr.ETIMEDOUT, err) { 167 return nil, nil 168 } 169 170 return nil, err 171 } 172 } 173 }