From 2fb1362883c72bc8d2779ac1fb214bb0b906cc0a Mon Sep 17 00:00:00 2001 From: Jiri Denemark Date: Thu, 22 Sep 2011 14:59:06 +0200 Subject: [PATCH] Implement keepalive protocol in remote driver --- src/remote/remote_driver.c | 52 ++++++++++++++++++++++ src/rpc/virnetclient.c | 88 ++++++++++++++++++++++++++++++++++++-- src/rpc/virnetclient.h | 5 +++ 3 files changed, 141 insertions(+), 4 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 94fd3e7a42..e0cd828921 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -68,6 +68,7 @@ #endif static int inside_daemon = 0; +static virDriverPtr remoteDriver = NULL; struct private_data { virMutex lock; @@ -84,6 +85,7 @@ struct private_data { char *type; /* Cached return from remoteType. */ int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ + bool serverKeepAlive; /* Does server support keepalive protocol? */ virDomainEventStatePtr domainEventState; }; @@ -667,6 +669,26 @@ doRemoteOpen (virConnectPtr conn, if (remoteAuthenticate(conn, priv, auth, authtype) == -1) goto failed; + if (virNetClientKeepAliveIsSupported(priv->client)) { + remote_supports_feature_args args = + { VIR_DRV_FEATURE_PROGRAM_KEEPALIVE }; + remote_supports_feature_ret ret = { 0 }; + int rc; + + rc = call(conn, priv, 0, REMOTE_PROC_SUPPORTS_FEATURE, + (xdrproc_t)xdr_remote_supports_feature_args, (char *) &args, + (xdrproc_t)xdr_remote_supports_feature_ret, (char *) &ret); + if (rc == -1) + goto failed; + + if (ret.supported) { + priv->serverKeepAlive = true; + } else { + VIR_INFO("Disabling keepalive protocol since it is not supported" + " by the server"); + } + } + /* Finally we can call the remote side's open function. */ { remote_open_args args = { &name, flags }; @@ -4180,6 +4202,33 @@ done: } +static int +remoteSetKeepAlive(virConnectPtr conn, int interval, unsigned int count) +{ + struct private_data *priv = conn->privateData; + int ret = -1; + + remoteDriverLock(priv); + if (!virNetClientKeepAliveIsSupported(priv->client)) { + remoteError(VIR_ERR_INTERNAL_ERROR, "%s", + _("the caller doesn't support keepalive protocol;" + " perhaps it's missing event loop implementation")); + goto cleanup; + } + + if (!priv->serverKeepAlive) { + ret = 1; + goto cleanup; + } + + ret = virNetClientKeepAliveStart(priv->client, interval, count); + +cleanup: + remoteDriverUnlock(priv); + return ret; +} + + #include "remote_client_bodies.h" #include "qemu_client_bodies.h" @@ -4550,6 +4599,7 @@ static virDriver remote_driver = { .domainGetBlockJobInfo = remoteDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ + .setKeepAlive = remoteSetKeepAlive, /* 0.9.7 */ }; static virNetworkDriver network_driver = { @@ -4700,6 +4750,8 @@ static virStateDriver state_driver = { int remoteRegister (void) { + remoteDriver = &remote_driver; + if (virRegisterDriver (&remote_driver) == -1) return -1; if (virRegisterNetworkDriver (&network_driver) == -1) return -1; if (virRegisterInterfaceDriver (&interface_driver) == -1) return -1; diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c index a358d4cc48..3c97ad9133 100644 --- a/src/rpc/virnetclient.c +++ b/src/rpc/virnetclient.c @@ -29,6 +29,7 @@ #include "virnetclient.h" #include "virnetsocket.h" +#include "virkeepalive.h" #include "memory.h" #include "threads.h" #include "virfile.h" @@ -102,11 +103,12 @@ struct _virNetClient { size_t nstreams; virNetClientStreamPtr *streams; + virKeepAlivePtr keepalive; bool wantClose; }; -void virNetClientRequestClose(virNetClientPtr client); +static void virNetClientRequestClose(virNetClientPtr client); static void virNetClientLock(virNetClientPtr client) { @@ -222,11 +224,56 @@ static void virNetClientEventFree(void *opaque) virNetClientFree(client); } +bool +virNetClientKeepAliveIsSupported(virNetClientPtr client) +{ + bool supported; + + virNetClientLock(client); + supported = !!client->keepalive; + virNetClientUnlock(client); + + return supported; +} + +int +virNetClientKeepAliveStart(virNetClientPtr client, + int interval, + unsigned int count) +{ + int ret; + + virNetClientLock(client); + ret = virKeepAliveStart(client->keepalive, interval, count); + virNetClientUnlock(client); + + return ret; +} + +static void +virNetClientKeepAliveDeadCB(void *opaque) +{ + virNetClientRequestClose(opaque); +} + +static int +virNetClientKeepAliveSendCB(void *opaque, + virNetMessagePtr msg) +{ + int ret; + + ret = virNetClientSendNonBlock(opaque, msg); + if (ret != -1 && ret != 1) + virNetMessageFree(msg); + return ret; +} + static virNetClientPtr virNetClientNew(virNetSocketPtr sock, const char *hostname) { virNetClientPtr client = NULL; int wakeupFD[2] = { -1, -1 }; + virKeepAlivePtr ka = NULL; if (pipe2(wakeupFD, O_CLOEXEC) < 0) { virReportSystemError(errno, "%s", @@ -259,13 +306,24 @@ static virNetClientPtr virNetClientNew(virNetSocketPtr sock, client, virNetClientEventFree) < 0) { client->refs--; - VIR_DEBUG("Failed to add event watch, disabling events"); + VIR_DEBUG("Failed to add event watch, disabling events and support for" + " keepalive messages"); + } else { + /* Keepalive protocol consists of async messages so it can only be used + * if the client supports them */ + if (!(ka = virKeepAliveNew(-1, 0, client, + virNetClientKeepAliveSendCB, + virNetClientKeepAliveDeadCB, + virNetClientEventFree))) + goto error; + /* keepalive object has a reference to client */ + client->refs++; } + client->keepalive = ka; PROBE(RPC_CLIENT_NEW, "client=%p refs=%d sock=%p", client, client->refs, client->sock); - return client; no_memory: @@ -273,6 +331,10 @@ no_memory: error: VIR_FORCE_CLOSE(wakeupFD[0]); VIR_FORCE_CLOSE(wakeupFD[1]); + if (ka) { + virKeepAliveStop(ka); + virKeepAliveFree(ka); + } virNetClientFree(client); return NULL; } @@ -416,6 +478,8 @@ void virNetClientFree(virNetClientPtr client) static void virNetClientCloseLocked(virNetClientPtr client) { + virKeepAlivePtr ka; + VIR_DEBUG("client=%p, sock=%p", client, client->sock); if (!client->sock) @@ -430,7 +494,20 @@ virNetClientCloseLocked(virNetClientPtr client) virNetSASLSessionFree(client->sasl); client->sasl = NULL; #endif + ka = client->keepalive; + client->keepalive = NULL; client->wantClose = false; + + if (ka) { + client->refs++; + virNetClientUnlock(client); + + virKeepAliveStop(ka); + virKeepAliveFree(ka); + + virNetClientLock(client); + client->refs--; + } } void virNetClientClose(virNetClientPtr client) @@ -443,7 +520,7 @@ void virNetClientClose(virNetClientPtr client) virNetClientUnlock(client); } -void +static void virNetClientRequestClose(virNetClientPtr client) { VIR_DEBUG("client=%p", client); @@ -844,6 +921,9 @@ virNetClientCallDispatch(virNetClientPtr client) client->msg.header.prog, client->msg.header.vers, client->msg.header.proc, client->msg.header.type, client->msg.header.status, client->msg.header.serial); + if (virKeepAliveCheckMessage(client->keepalive, &client->msg)) + return 0; + switch (client->msg.header.type) { case VIR_NET_REPLY: /* Normal RPC replies */ case VIR_NET_REPLY_WITH_FDS: /* Normal RPC replies with FDs */ diff --git a/src/rpc/virnetclient.h b/src/rpc/virnetclient.h index 71db543303..530b0bb81f 100644 --- a/src/rpc/virnetclient.h +++ b/src/rpc/virnetclient.h @@ -95,4 +95,9 @@ int virNetClientGetTLSKeySize(virNetClientPtr client); void virNetClientFree(virNetClientPtr client); void virNetClientClose(virNetClientPtr client); +bool virNetClientKeepAliveIsSupported(virNetClientPtr client); +int virNetClientKeepAliveStart(virNetClientPtr client, + int interval, + unsigned int count); + #endif /* __VIR_NET_CLIENT_H__ */ -- GitLab