github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/shim/epoll.go (about) 1 // Copyright 2018 The containerd Authors. 2 // Copyright 2018 The gVisor Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 //go:build linux 17 // +build linux 18 19 package shim 20 21 import ( 22 "context" 23 "fmt" 24 "sync" 25 26 "github.com/containerd/cgroups" 27 "github.com/containerd/containerd/events" 28 "github.com/containerd/containerd/runtime" 29 "golang.org/x/sys/unix" 30 ) 31 32 func newOOMEpoller(publisher events.Publisher) (*epoller, error) { 33 fd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) 34 if err != nil { 35 return nil, err 36 } 37 return &epoller{ 38 fd: fd, 39 publisher: publisher, 40 set: make(map[uintptr]*item), 41 }, nil 42 } 43 44 type epoller struct { 45 mu sync.Mutex 46 47 fd int 48 publisher events.Publisher 49 set map[uintptr]*item 50 } 51 52 type item struct { 53 id string 54 cg cgroups.Cgroup 55 } 56 57 func (e *epoller) Close() error { 58 return unix.Close(e.fd) 59 } 60 61 func (e *epoller) run(ctx context.Context) { 62 var events [128]unix.EpollEvent 63 for { 64 select { 65 case <-ctx.Done(): 66 e.Close() 67 return 68 default: 69 n, err := unix.EpollWait(e.fd, events[:], -1) 70 if err != nil { 71 if err == unix.EINTR || err == unix.EAGAIN { 72 continue 73 } 74 // Should not happen. 75 panic(fmt.Errorf("cgroups: epoll wait: %w", err)) 76 } 77 for i := 0; i < n; i++ { 78 e.process(ctx, uintptr(events[i].Fd)) 79 } 80 } 81 } 82 } 83 84 func (e *epoller) add(id string, cgx any) error { 85 e.mu.Lock() 86 defer e.mu.Unlock() 87 cg, ok := cgx.(cgroups.Cgroup) 88 if !ok { 89 return fmt.Errorf("expected cgroups.Cgroup, got: %T", cgx) 90 } 91 fd, err := cg.OOMEventFD() 92 if err != nil { 93 return err 94 } 95 e.set[fd] = &item{ 96 id: id, 97 cg: cg, 98 } 99 event := unix.EpollEvent{ 100 Fd: int32(fd), 101 Events: unix.EPOLLHUP | unix.EPOLLIN | unix.EPOLLERR, 102 } 103 return unix.EpollCtl(e.fd, unix.EPOLL_CTL_ADD, int(fd), &event) 104 } 105 106 func (e *epoller) process(ctx context.Context, fd uintptr) { 107 flush(fd) 108 e.mu.Lock() 109 i, ok := e.set[fd] 110 if !ok { 111 e.mu.Unlock() 112 return 113 } 114 e.mu.Unlock() 115 if i.cg.State() == cgroups.Deleted { 116 e.mu.Lock() 117 delete(e.set, fd) 118 e.mu.Unlock() 119 unix.Close(int(fd)) 120 return 121 } 122 if err := e.publisher.Publish(ctx, runtime.TaskOOMEventTopic, &TaskOOM{ 123 ContainerID: i.id, 124 }); err != nil { 125 // Should not happen. 126 panic(fmt.Errorf("publish OOM event: %w", err)) 127 } 128 } 129 130 func flush(fd uintptr) error { 131 var buf [8]byte 132 _, err := unix.Read(int(fd), buf[:]) 133 return err 134 }