rpc: finish all threads before exiting main loop

Currently we have issues like [1] on libvirtd shutdown as we cleanup while RPC
and other threads are still running. Let's finish all threads other then main
before cleanup.

The approach to finish threads is suggested in [2]. In order to finish RPC
threads serving API calls we let the event loop run but stop accepting new API
calls and block processing any pending API calls. We also inform all drivers of
shutdown so they can prepare for shutdown too. Then we wait for all RPC threads
and driver's background thread to finish. If finishing takes more then 15s we
just exit as we can't safely cleanup in time.

[1] https://bugzilla.redhat.com/show_bug.cgi?id=1828207
[2] https://www.redhat.com/archives/libvir-list/2020-April/msg01328.html

Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy@virtuozzo.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Nikolay Shirokovskiy 2020-07-23 09:53:04 +03:00
parent b776dfa8e8
commit 94e45d1042
2 changed files with 83 additions and 4 deletions

View File

@ -1193,6 +1193,9 @@ int main(int argc, char **argv) {
#endif
/* Run event loop. */
virNetDaemonSetShutdownCallbacks(dmn,
virStateShutdownPrepare,
virStateShutdownWait);
virNetDaemonRun(dmn);
ret = 0;
@ -1201,9 +1204,6 @@ int main(int argc, char **argv) {
0, "shutdown", NULL, NULL);
cleanup:
/* Keep cleanup order in inverse order of startup */
virNetDaemonClose(dmn);
virNetlinkEventServiceStopAll();
if (driversInitialized) {

View File

@ -71,7 +71,10 @@ struct _virNetDaemon {
virNetDaemonShutdownCallback shutdownPrepareCb;
virNetDaemonShutdownCallback shutdownWaitCb;
int finishTimer;
bool quit;
bool finished;
bool graceful;
unsigned int autoShutdownTimeout;
size_t autoShutdownInhibitions;
@ -82,6 +85,11 @@ struct _virNetDaemon {
static virClassPtr virNetDaemonClass;
static int
daemonServerClose(void *payload,
const void *key G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED);
static void
virNetDaemonDispose(void *obj)
{
@ -798,11 +806,53 @@ daemonServerProcessClients(void *payload,
return 0;
}
static int
daemonServerShutdownWait(void *payload,
const void *key G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
virNetServerPtr srv = payload;
virNetServerShutdownWait(srv);
return 0;
}
static void
daemonShutdownWait(void *opaque)
{
virNetDaemonPtr dmn = opaque;
bool graceful = false;
virHashForEach(dmn->servers, daemonServerShutdownWait, NULL);
if (dmn->shutdownWaitCb && dmn->shutdownWaitCb() < 0)
goto finish;
graceful = true;
finish:
virObjectLock(dmn);
dmn->graceful = graceful;
virEventUpdateTimeout(dmn->finishTimer, 0);
virObjectUnlock(dmn);
}
static void
virNetDaemonFinishTimer(int timerid G_GNUC_UNUSED,
void *opaque)
{
virNetDaemonPtr dmn = opaque;
virObjectLock(dmn);
dmn->finished = true;
virObjectUnlock(dmn);
}
void
virNetDaemonRun(virNetDaemonPtr dmn)
{
int timerid = -1;
bool timerActive = false;
virThread shutdownThread;
virObjectLock(dmn);
@ -813,6 +863,9 @@ virNetDaemonRun(virNetDaemonPtr dmn)
}
dmn->quit = false;
dmn->finishTimer = -1;
dmn->finished = false;
dmn->graceful = false;
if (dmn->autoShutdownTimeout &&
(timerid = virEventAddTimeout(-1,
@ -828,7 +881,7 @@ virNetDaemonRun(virNetDaemonPtr dmn)
virSystemdNotifyStartup();
VIR_DEBUG("dmn=%p quit=%d", dmn, dmn->quit);
while (!dmn->quit) {
while (!dmn->finished) {
/* A shutdown timeout is specified, so check
* if any drivers have active state, if not
* shutdown after timeout seconds
@ -859,6 +912,32 @@ virNetDaemonRun(virNetDaemonPtr dmn)
virObjectLock(dmn);
virHashForEach(dmn->servers, daemonServerProcessClients, NULL);
if (dmn->quit && dmn->finishTimer == -1) {
virHashForEach(dmn->servers, daemonServerClose, NULL);
if (dmn->shutdownPrepareCb && dmn->shutdownPrepareCb() < 0)
break;
if ((dmn->finishTimer = virEventAddTimeout(30 * 1000,
virNetDaemonFinishTimer,
dmn, NULL)) < 0) {
VIR_WARN("Failed to register finish timer.");
break;
}
if (virThreadCreateFull(&shutdownThread, true, daemonShutdownWait,
"daemon-shutdown", false, dmn) < 0) {
VIR_WARN("Failed to register join thread.");
break;
}
}
}
if (dmn->graceful) {
virThreadJoin(&shutdownThread);
} else {
VIR_WARN("Make forcefull daemon shutdown");
exit(EXIT_FAILURE);
}
cleanup: