[libvirt] [PATCH] remote: Fix libvirtd service memory leak when libvirt client was terminated unexpectly
xinhua.Cao
caoxinhua at huawei.com
Mon Nov 6 01:23:06 UTC 2017
在 2017/11/3 1:29, Martin Kletzander 写道:
> On Tue, Oct 24, 2017 at 01:52:28PM +0800, xinhua.Cao wrote:
>> base on commit 2033e8cc119454bc4273e8a41e66c899c60ba58b and
>> fe8f1c8b8650c8ab5e7d27338f4538582651bd14, we solve libvirt coredump
>> problem, but it introduce a memory leak sense.
>
> The first one is just a syntax sugar, it introduces no functional change.
>
yes, this patch is OK. because first patch and second patch have same
relationship, so I point those two patch.
>> the sense follow
>> 1. one client register a domain event such as reboot event
>> 2. and client was terminated unexpectly, then this client will not
>> free at libvirtd service program.
>>
>> remoteDispatchConnectDomainEventCallbackRegisterAny reference the
>> client, but when client was terminated before it call deRegisterAny,
>> the reference of client will not reduced to zero. so the memory leak
>> take place. this patch will deRegister all event when client was close.
>
> Can you elaborate more on how does the client get terminated? Maybe
> the problem
> is that there is a way to terminate the client and not call the
> FreeFunc on it
> and the fact that it doesn't go through the right cleanup procedure
> should be
> what we should focus on?
>
such as kill -9 or client crash.
> Also please wrap the commit message as any other commit. See `git
> log` for
> reference.
OK, it will be correct at v2 patch
>
>> ---
>> daemon/remote.c | 47 +++++++++++++++++++++++++++++------------------
>> 1 file changed, 29 insertions(+), 18 deletions(-)
>>
>> diff --git a/daemon/remote.c b/daemon/remote.c
>> index 3f7d2d3..2b5a18b 100644
>> --- a/daemon/remote.c
>> +++ b/daemon/remote.c
>> @@ -1686,25 +1686,16 @@ void
>> remoteRelayConnectionClosedEvent(virConnectPtr conn ATTRIBUTE_UNUSED,
>> int r
>> VIR_WARN("unexpected %s event deregister failure",
>> name); \
>> } \
>> VIR_FREE(eventCallbacks); \
>> + neventCallbacks =
>> 0; \
>
> This is OK, ACK to this hunk. But I think this should be in a
> separate patch,
> probably.
>
OK, it will be at v2 patch
>> } while (0);
>>
>> -/*
>> - * You must hold lock for at least the client
>> - * We don't free stuff here, merely disconnect the client's
>> - * network socket & resources.
>> - * We keep the libvirt connection open until any async
>> - * jobs have finished, then clean it up elsewhere
>> - */
>> -void remoteClientFreeFunc(void *data)
>> +static void
>> +remoteFreePrivCallbacks(void *data)
>
> Why is it called Callbacks when it is not passed as a callback
> anywhere? Why
> does it take void *? Why does it not have a 'Client' in the name when it
> clearly works with a daemonClientPrivate data?
>
so can we use remoteClientFreePrivateCallbacks?
>> {
>> struct daemonClientPrivate *priv = data;
>>
>> /* Deregister event delivery callback */
>> - if (priv->conn) {
>> - virIdentityPtr sysident = virIdentityGetSystem();
>> -
>> - virIdentitySetCurrent(sysident);
>> -
>> + if (priv && priv->conn) {
>> DEREG_CB(priv->conn, priv->domainEventCallbacks,
>> priv->ndomainEventCallbacks,
>> virConnectDomainEventDeregisterAny, "domain");
>> @@ -1723,6 +1714,26 @@ void remoteClientFreeFunc(void *data)
>> DEREG_CB(priv->conn, priv->qemuEventCallbacks,
>> priv->nqemuEventCallbacks,
>> virConnectDomainQemuMonitorEventDeregister, "qemu
>> monitor");
>> + }
>> +}
>> +#undef DEREG_CB
>> +
>> +/*
>> + * You must hold lock for at least the client
>> + * We don't free stuff here, merely disconnect the client's
>> + * network socket & resources.
>> + * We keep the libvirt connection open until any async
>> + * jobs have finished, then clean it up elsewhere
>> + */
>> +void remoteClientFreeFunc(void *data)
>> +{
>> + struct daemonClientPrivate *priv = data;
>> +
>> + if (priv) {
>> + virIdentityPtr sysident = virIdentityGetSystem();
>> +
>> + virIdentitySetCurrent(sysident);
>> + remoteFreePrivCallbacks(priv);
>>
>> if (priv->closeRegistered) {
>> if (virConnectUnregisterCloseCallback(priv->conn,
>
> Why don't you also remove this callback in the new function? Does the
> close
> event not get propagated when you move it there?
>
OK, it will be at v2 patch
>> @@ -1734,18 +1745,18 @@ void remoteClientFreeFunc(void *data)
>>
>> virIdentitySetCurrent(NULL);
>> virObjectUnref(sysident);
>> + VIR_FREE(priv);
>> }
>> -
>> - VIR_FREE(priv);
>> }
>> -#undef DEREG_CB
>> -
>>
>> static void remoteClientCloseFunc(virNetServerClientPtr client)
>> {
>> struct daemonClientPrivate *priv =
>> virNetServerClientGetPrivateData(client);
>>
>> - daemonRemoveAllClientStreams(priv->streams);
>> + if (priv) {
>
> Can it happen that priv is NULL? It should only be freed when the
> client is
> freed in which case this function should not be called at all. This is a
> warning light for me, if you encountered priv == NULL in this
> function, then it
> signals that there is probably a problem somewhere else as well.
>
there have no way to take place "priv is NULL", I check it only because
my habit. I will delete it at v2 patch.
>> + daemonRemoveAllClientStreams(priv->streams);
>> + remoteFreePrivCallbacks(priv);
>> + }
>> }
>>
>
> Generally, I'm OK with splitting the Free function to two of them, one
> doing the
> closing part and one freeing the data (similarly to what I suggested
> in another
> thread for virNetDaemonDispose() just now), but this patch does it in
> a very
> weird way.
More information about the libvir-list
mailing list