diff options
author | Jielong Zhou <jielong.zjl@antfin.com> | 2021-08-09 20:01:05 +0800 |
---|---|---|
committer | Jielong Zhou <jielong.zjl@antfin.com> | 2021-08-09 20:07:58 +0800 |
commit | 57da3c7ddd50f9a909d9b0022fe63eb201a2ca0e (patch) | |
tree | 9a737ecb4f8d84eb2f58624f58d2ac93de46555d /test/syscalls/linux/epoll.cc | |
parent | c07dc3828a0330a3804514094d45e6362ae2de30 (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.cc | 44 |
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 |