1 /*
2  * Copyright (C) 2022 The Android Open Source Project
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  *      http://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 
17 #include "common/libs/fs/epoll.h"
18 
19 #include <sys/epoll.h>
20 
21 #include <memory>
22 #include <mutex>
23 #include <optional>
24 #include <set>
25 #include <shared_mutex>
26 #include <string>
27 
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/result.h"
30 
31 namespace cuttlefish {
32 
Create()33 Result<Epoll> Epoll::Create() {
34   int fd = epoll_create1(EPOLL_CLOEXEC);
35   if (fd == -1) {
36     return CF_ERRNO("Failed to create epoll");
37   }
38   SharedFD shared{std::shared_ptr<FileInstance>(new FileInstance(fd, 0))};
39   return Epoll(shared);
40 }
41 
42 Epoll::Epoll() = default;
43 
Epoll(SharedFD epoll_fd)44 Epoll::Epoll(SharedFD epoll_fd) : epoll_fd_(epoll_fd) {}
45 
Epoll(Epoll && other)46 Epoll::Epoll(Epoll&& other) {
47   std::unique_lock own_watched(watched_mutex_, std::defer_lock);
48   std::unique_lock own_epoll(epoll_mutex_, std::defer_lock);
49   std::unique_lock other_epoll(other.epoll_mutex_, std::defer_lock);
50   std::unique_lock other_watched(other.watched_mutex_, std::defer_lock);
51   std::lock(own_watched, own_epoll, other_epoll, other_watched);
52 
53   epoll_fd_ = std::move(other.epoll_fd_);
54   watched_ = std::move(other.watched_);
55 }
56 
operator =(Epoll && other)57 Epoll& Epoll::operator=(Epoll&& other) {
58   std::unique_lock own_watched(watched_mutex_, std::defer_lock);
59   std::unique_lock own_epoll(epoll_mutex_, std::defer_lock);
60   std::unique_lock other_epoll(other.epoll_mutex_, std::defer_lock);
61   std::unique_lock other_watched(other.watched_mutex_, std::defer_lock);
62   std::lock(own_watched, own_epoll, other_epoll, other_watched);
63 
64   epoll_fd_ = std::move(other.epoll_fd_);
65   watched_ = std::move(other.watched_);
66   return *this;
67 }
68 
Add(SharedFD fd,uint32_t events)69 Result<void> Epoll::Add(SharedFD fd, uint32_t events) {
70   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
71   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
72   std::lock(watched_lock, epoll_lock);
73   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
74 
75   if (watched_.count(fd) != 0) {
76     return CF_ERRNO("Watched set already contains fd");
77   }
78   epoll_event event;
79   event.events = events;
80   event.data.fd = fd->fd_;
81   int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_ADD, fd->fd_, &event);
82   if (success != 0 && errno == EEXIST) {
83     // We're already tracking this fd, don't drop it from the set.
84     return CF_ERRNO("epoll_ctl: File descriptor was already present");
85   } else if (success != 0) {
86     return CF_ERRNO("epoll_ctl: Add failed");
87   }
88   watched_.insert(fd);
89   return {};
90 }
91 
AddOrModify(SharedFD fd,uint32_t events)92 Result<void> Epoll::AddOrModify(SharedFD fd, uint32_t events) {
93   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
94   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
95   std::lock(watched_lock, epoll_lock);
96   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
97 
98   epoll_event event;
99   event.events = events;
100   event.data.fd = fd->fd_;
101   int operation = watched_.count(fd) == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
102   int success = epoll_ctl(epoll_fd_->fd_, operation, fd->fd_, &event);
103   if (success != 0) {
104     std::string operation_str = operation == EPOLL_CTL_ADD ? "add" : "modify";
105     return CF_ERRNO("epoll_ctl: Operation " << operation_str << " failed");
106   }
107   watched_.insert(fd);
108   return {};
109 }
110 
Modify(SharedFD fd,uint32_t events)111 Result<void> Epoll::Modify(SharedFD fd, uint32_t events) {
112   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
113   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
114   std::lock(watched_lock, epoll_lock);
115   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
116 
117   if (watched_.count(fd) == 0) {
118     return CF_ERR("Watched set did not contain fd");
119   }
120   epoll_event event;
121   event.events = events;
122   event.data.fd = fd->fd_;
123   int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_MOD, fd->fd_, &event);
124   if (success != 0) {
125     return CF_ERRNO("epoll_ctl: Modify failed");
126   }
127   return {};
128 }
129 
Delete(SharedFD fd)130 Result<void> Epoll::Delete(SharedFD fd) {
131   std::unique_lock watched_lock(watched_mutex_, std::defer_lock);
132   std::shared_lock epoll_lock(epoll_mutex_, std::defer_lock);
133   std::lock(watched_lock, epoll_lock);
134   CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
135 
136   if (watched_.count(fd) == 0) {
137     return CF_ERR("Watched set did not contain fd");
138   }
139   int success = epoll_ctl(epoll_fd_->fd_, EPOLL_CTL_DEL, fd->fd_, nullptr);
140   if (success != 0) {
141     return CF_ERRNO("epoll_ctl: Delete failed");
142   }
143   watched_.erase(fd);
144   return {};
145 }
146 
Wait()147 Result<std::optional<EpollEvent>> Epoll::Wait() {
148   epoll_event event;
149   int success;
150   {
151     std::shared_lock lock(epoll_mutex_);
152     CF_EXPECT(epoll_fd_->IsOpen(), "Empty Epoll instance");
153     success = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_->fd_, &event, 1, -1));
154   }
155   if (success == -1) {
156     return CF_ERRNO("epoll_wait failed");
157   } else if (success == 0) {
158     return {};
159   } else if (success != 1) {
160     return CF_ERR("epoll_wait returned an unexpected value");
161   }
162   EpollEvent ret;
163   ret.events = event.events;
164   std::shared_lock lock(watched_mutex_);
165   for (const auto& watched : watched_) {
166     if (watched->fd_ == event.data.fd) {
167       ret.fd = watched;
168       break;
169     }
170   }
171   if (!ret.fd->IsOpen()) {
172     // Couldn't find the matching SharedFD to the file descriptor. We probably
173     // lost the race to lock watched_mutex_ against a delete call. Treat this
174     // as a spurious wakeup.
175     return {};
176   }
177   return ret;
178 }
179 
180 }  // namespace cuttlefish
181