From fcd82c8ebf671e268fa16da3f172ea6679eb45ec Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 30 Mar 2022 18:44:12 -0700 Subject: [PATCH] epoll: notify waiters if ReadEvents adds events ReadEvents is racy with concurrent notifications and it has a code path that can add events to the ready queue. In such cases, it has to notify waiters. Users of ReadEvents() registers for events and then call ReadEvents. They expect that if ReadEvents returns nothing, they will get a notification when there will be something to read. PiperOrigin-RevId: 438449859 --- pkg/sentry/kernel/epoll/epoll.go | 10 ++++++++++ pkg/sentry/vfs/epoll.go | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/pkg/sentry/kernel/epoll/epoll.go b/pkg/sentry/kernel/epoll/epoll.go index fca9f7a91..e56f422c8 100644 --- a/pkg/sentry/kernel/epoll/epoll.go +++ b/pkg/sentry/kernel/epoll/epoll.go @@ -212,6 +212,7 @@ func (e *EventPoll) eventsAvailable() bool { return false } defer func() { + notify := true e.listsMu.Lock() e.readyList.PushFrontList(&readyList) var next *pollEntry @@ -221,12 +222,16 @@ func (e *EventPoll) eventsAvailable() bool { // entry.NotifyEvent() was called while we were running. waitingList.Remove(entry) e.readyList.PushBack(entry) + notify = true } else { entry.curList = &e.waitingList } } e.waitingList.PushBackList(&waitingList) e.listsMu.Unlock() + if notify { + e.Notify(waiter.ReadableEvents) + } }() for it := readyList.Front(); it != nil; { @@ -287,6 +292,7 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent { return nil } defer func() { + notify := false e.listsMu.Lock() e.readyList.PushFrontList(&readyList) var next *pollEntry @@ -296,6 +302,7 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent { // entry.NotifyEvent() was called while we were running. waitingList.Remove(entry) e.readyList.PushBack(entry) + notify = true } else { entry.curList = &e.waitingList } @@ -307,6 +314,9 @@ func (e *EventPoll) ReadEvents(max int) []linux.EpollEvent { } e.disabledList.PushBackList(&disabledList) e.listsMu.Unlock() + if notify { + e.Notify(waiter.ReadableEvents) + } }() // Go through all entries we believe may be ready. diff --git a/pkg/sentry/vfs/epoll.go b/pkg/sentry/vfs/epoll.go index 6041f8ff6..dd885e579 100644 --- a/pkg/sentry/vfs/epoll.go +++ b/pkg/sentry/vfs/epoll.go @@ -170,6 +170,7 @@ func (ep *EpollInstance) Readiness(mask waiter.EventMask) waiter.EventMask { return 0 } defer func() { + notify := false ep.readyMu.Lock() ep.ready.PushFrontList(&ready) var next *epollInterest @@ -179,11 +180,15 @@ func (ep *EpollInstance) Readiness(mask waiter.EventMask) waiter.EventMask { // epi.NotifyEvent() was called while we were running. notReady.Remove(epi) ep.ready.PushBack(epi) + notify = true } else { epi.ready = false } } ep.readyMu.Unlock() + if notify { + ep.q.Notify(waiter.ReadableEvents) + } }() var next *epollInterest @@ -428,6 +433,7 @@ func (ep *EpollInstance) ReadEvents(events []linux.EpollEvent, maxEvents int) [] return nil } defer func() { + notify := false ep.readyMu.Lock() // epollInterests that we never checked are re-inserted at the start of // ep.ready. epollInterests that were ready are re-inserted at the end @@ -440,12 +446,16 @@ func (ep *EpollInstance) ReadEvents(events []linux.EpollEvent, maxEvents int) [] // epi.NotifyEvent() was called while we were running. notReady.Remove(epi) ep.ready.PushBack(epi) + notify = true } else { epi.ready = false } } ep.ready.PushBackList(&requeue) ep.readyMu.Unlock() + if notify { + ep.q.Notify(waiter.ReadableEvents) + } }() i := 0