Kernel: Fix epoll deadlock

If epoll_wait and epoll_notify were called at the same time, there was a
possible deadlock when epoll was confirming the event from the inode
This commit is contained in:
Bananymous 2025-05-29 22:08:24 +03:00
parent 1bd454b8fd
commit 9f4b451501
2 changed files with 15 additions and 0 deletions

View File

@ -184,6 +184,7 @@ namespace Kernel
BAN::WeakPtr<SharedFileData> m_shared_region;
Mutex m_epoll_mutex;
BAN::LinkedList<class Epoll*> m_epolls;
friend class Epoll;
friend class FileBackedRegion;
friend class OpenFileDescriptorSet;
friend class SharedFileData;

View File

@ -60,6 +60,8 @@ namespace Kernel
for (;;)
{
bool failed_lock = false;
{
LockGuard _(m_mutex);
for (auto it = m_ready_events.begin(); it != m_ready_events.end() && count < event_span.size();)
@ -70,6 +72,14 @@ namespace Kernel
const uint32_t listen_mask = (listen.events & (EPOLLIN | EPOLLOUT)) | EPOLLERR | EPOLLHUP;
events &= listen_mask;
// This prevents a possible deadlock
if (!inode->m_mutex.try_lock())
{
failed_lock = true;
continue;
}
#define CHECK_EVENT_BIT(mask, func) \
if ((events & mask) && !inode->func()) \
events &= ~mask;
@ -79,6 +89,8 @@ namespace Kernel
CHECK_EVENT_BIT(EPOLLHUP, has_hungup);
#undef CHECK_EVENT_BIT
inode->m_mutex.unlock();
if (events == 0)
{
m_ready_events.remove(it);
@ -107,6 +119,8 @@ namespace Kernel
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
if (current_ns >= waketime_ns)
break;
if (failed_lock)
continue;
const uint64_t timeout_ns = BAN::Math::min<uint64_t>(100'000'000, waketime_ns - current_ns);
TRY(Thread::current().block_or_eintr_or_timeout_ns(m_thread_blocker, timeout_ns, false));
}