summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/epoll.cc
diff options
context:
space:
mode:
authorJielong Zhou <jielong.zjl@antfin.com>2021-08-09 20:01:05 +0800
committerJielong Zhou <jielong.zjl@antfin.com>2021-08-09 20:07:58 +0800
commit57da3c7ddd50f9a909d9b0022fe63eb201a2ca0e (patch)
tree9a737ecb4f8d84eb2f58624f58d2ac93de46555d /test/syscalls/linux/epoll.cc
parentc07dc3828a0330a3804514094d45e6362ae2de30 (diff)
vfs2/epoll: fix missing event trigger in epoll-in-epoll case
In some cases, epoll fd would be registered in another epoll fd. Process may call epoll_wait on the upper layer epoll fd, and the lower layer epoll fd should generate EPOLLIN event if itself get any event. But in VFS2, events generated for epoll fd could only be cleaned when (*EpollInstance).ReadEvents is called. And this function is only called when epoll_wait on the epoll fd. Therefore, when epoll_wait on the upper layer epoll fd, the events generated in lower layer epoll fd would not be cleaned even if it's not valid anymore, and lower layer epoll fd would not report event to upper layer even if new event is triggered. In this commit, (*EpollInstance).Readiness would also clean invalid events. So, when epoll_wait on the upper layer epoll fd, Readiness function called on lower layer epoll fd would clean invalid events. And lower layer could report event to upper layer if new event is triggered. A syscall test case is added to verify the commit. Fixes https://github.com/google/gvisor/issues/6427 Signed-off-by: Jielong Zhou <jielong.zjl@antgroup.com>
Diffstat (limited to 'test/syscalls/linux/epoll.cc')
-rw-r--r--test/syscalls/linux/epoll.cc44
1 files changed, 44 insertions, 0 deletions
diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc
index 3ef8b0327..89b16d168 100644
--- a/test/syscalls/linux/epoll.cc
+++ b/test/syscalls/linux/epoll.cc
@@ -496,6 +496,50 @@ TEST(EpollTest, PipeReaderHupAfterWriterClosed) {
EXPECT_EQ(result[0].data.u64, kMagicConstant);
}
+TEST(EpollTest, DoubleLayerEpoll) {
+ int listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ struct sockaddr_in sa;
+ sa.sin_family = AF_INET;
+ inet_aton("127.0.0.1", &sa.sin_addr);
+ sa.sin_port = htons(8888);
+ bind(listen_fd, (struct sockaddr *)&sa, sizeof(sa));
+ listen(listen_fd, 1);
+
+ auto epfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
+ ASSERT_NO_ERRNO(RegisterEpollFD(epfd1.get(), listen_fd, EPOLLIN|EPOLLHUP, listen_fd));
+
+ auto epfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
+ ASSERT_NO_ERRNO(RegisterEpollFD(epfd2.get(), epfd1.get(), EPOLLIN|EPOLLHUP, epfd1.get()));
+
+ struct epoll_event ret_events[2];
+
+ // first time
+ ScopedThread thread1([&sa]() {
+ sleep(1);
+ int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ASSERT_THAT(connect(sock, (struct sockaddr *)&sa, sizeof(sa)), SyscallSucceeds());
+ });
+ ASSERT_THAT(epoll_wait(epfd2.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(ret_events[0].data.fd, epfd1.get());
+ ASSERT_THAT(epoll_wait(epfd1.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(ret_events[0].data.fd, listen_fd);
+ int server_fd = accept(listen_fd, NULL, NULL);
+ ASSERT_THAT(close(server_fd), SyscallSucceeds());
+
+ // second time to check if event in epfd1 is cleaned correctly
+ ScopedThread thread2([&sa]() {
+ sleep(1);
+ int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ ASSERT_THAT(connect(sock, (struct sockaddr *)&sa, sizeof(sa)), SyscallSucceeds());
+ });
+ ASSERT_THAT(epoll_wait(epfd2.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(ret_events[0].data.fd, epfd1.get());
+ ASSERT_THAT(epoll_wait(epfd1.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(ret_events[0].data.fd, listen_fd);
+ server_fd = accept(listen_fd, NULL, NULL);
+ ASSERT_THAT(close(server_fd), SyscallSucceeds());
+}
+
} // namespace
} // namespace testing