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