Skip to content

Commit e62da98

Browse files
stefanhaRHkevmw
authored andcommitted
aio-posix: fix race between epoll upgrade and aio_set_fd_handler()
If another thread calls aio_set_fd_handler() while the IOThread event loop is upgrading from ppoll(2) to epoll(7) then we might miss new AioHandlers. The epollfd will not monitor the new AioHandler's fd, resulting in hangs. Take the AioHandler list lock while upgrading to epoll. This prevents AioHandlers from changing while epoll is being set up. If we cannot lock because we're in a nested event loop, then don't upgrade to epoll (it will happen next time we're not in a nested call). The downside to taking the lock is that the aio_set_fd_handler() thread has to wait until the epoll upgrade is finished, which involves many epoll_ctl(2) system calls. However, this scenario is rare and I couldn't think of another solution that is still simple. Reported-by: Qing Wang <[email protected]> Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2090998 Cc: Paolo Bonzini <[email protected]> Cc: Fam Zheng <[email protected]> Signed-off-by: Stefan Hajnoczi <[email protected]> Message-Id: <[email protected]> Reviewed-by: Kevin Wolf <[email protected]> Signed-off-by: Kevin Wolf <[email protected]>
1 parent 2957dc4 commit e62da98

File tree

1 file changed

+18
-7
lines changed

1 file changed

+18
-7
lines changed

util/fdmon-epoll.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ static bool fdmon_epoll_try_enable(AioContext *ctx)
127127

128128
bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
129129
{
130+
bool ok;
131+
130132
if (ctx->epollfd < 0) {
131133
return false;
132134
}
@@ -136,14 +138,23 @@ bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
136138
return false;
137139
}
138140

139-
if (npfd >= EPOLL_ENABLE_THRESHOLD) {
140-
if (fdmon_epoll_try_enable(ctx)) {
141-
return true;
142-
} else {
143-
fdmon_epoll_disable(ctx);
144-
}
141+
if (npfd < EPOLL_ENABLE_THRESHOLD) {
142+
return false;
143+
}
144+
145+
/* The list must not change while we add fds to epoll */
146+
if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
147+
return false;
148+
}
149+
150+
ok = fdmon_epoll_try_enable(ctx);
151+
152+
qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
153+
154+
if (!ok) {
155+
fdmon_epoll_disable(ctx);
145156
}
146-
return false;
157+
return ok;
147158
}
148159

149160
void fdmon_epoll_setup(AioContext *ctx)

0 commit comments

Comments
 (0)