On 08/31/2018 02:32 PM, John Ferlan wrote:
On 08/27/2018 04:08 AM, Michal Privoznik wrote:
> This flag causes connection to be opened when needed (e.g. when
> calling virLockManagerLockDaemonAcquire for the first time) and
> instead of closing it at the end of such API store it in
> privateData so that it can be reused by later calls.
>
> This is needed because if a resource is acquired and connection
> is closed then virtlockd kills the registered PID (that's what
> virtlockd is designed to do). Therefore we will need the
> connection to open at drvAcquire and close not any sooner than
> drvRelease. However, as we will be locking files step-by-step we
> want to avoid opening new connection for every drvAcquire +
> drvRelease pair, so the connection is going to be shared even
> more than that. But more on that in next commit.
>
> Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
> ---
> src/locking/lock_driver.h | 7 +++++
> src/locking/lock_driver_lockd.c | 68 +++++++++++++++++++++++++++++++++++++----
> 2 files changed, 69 insertions(+), 6 deletions(-)
>
> diff --git a/src/locking/lock_driver.h b/src/locking/lock_driver.h
> index 59c4c3aac7..7e3ffc58b5 100644
> --- a/src/locking/lock_driver.h
> +++ b/src/locking/lock_driver.h
> @@ -67,8 +67,15 @@ typedef enum {
> VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY = (1 << 0),
> /* Prevent further lock/unlock calls from this process */
> VIR_LOCK_MANAGER_ACQUIRE_RESTRICT = (1 << 1),
> + /* Causes driver to keep connection open and reuse it for further use. */
> + VIR_LOCK_MANAGER_ACQUIRE_KEEP_OPEN = (1 << 2),
> } virLockManagerAcquireFlags;
>
> +typedef enum {
> + /* Reuse previously saved connection. */
> + VIR_LOCK_MANAGER_RELEASE_KEEP_OPEN = (1 << 0),
> +} virLockManagerReleaseFlags;
> +
> typedef enum {
> /* virLockManagerNew called for a freshly started domain */
> VIR_LOCK_MANAGER_NEW_STARTED = (1 << 0),
> diff --git a/src/locking/lock_driver_lockd.c b/src/locking/lock_driver_lockd.c
> index 4883e89ac6..14f9eae760 100644
> --- a/src/locking/lock_driver_lockd.c
> +++ b/src/locking/lock_driver_lockd.c
> @@ -76,6 +76,11 @@ struct _virLockManagerLockDaemonPrivate {
>
> size_t nresources;
> virLockManagerLockDaemonResourcePtr resources;
> +
> + int clientRefs;
> + virNetClientPtr client;
> + virNetClientProgramPtr program;
> + int counter;
> };
>
>
> @@ -440,6 +445,13 @@
virLockManagerLockDaemonPrivateFree(virLockManagerLockDaemonPrivatePtr priv)
> default:
> break;
> }
> +
> + if (priv->client) {
> + virNetClientClose(priv->client);
> + virObjectUnref(priv->client);
> + virObjectUnref(priv->program);
> + }
> +
How about a helper method that would do these?
I wonder now if the @priv should keep track of flags as well?
That way you could "always" use @priv to store the client, program, and
counter and then use helper methods to determine at close whether @flags
had the KEEP_OPEN or not instead of having KEEP_OPEN in various places.
I'm not
sure we can do this. There are places (e.g. regular disk content
locking code) where Acquire() and Release() are called separately and on
behalf of other process (domain is the owner of the lock). Definitely we
do not want any connection sharing there. Therefore I'm failing to see
how would Acquire() determine if it should save the connection for later
use or not.
> VIR_FREE(priv);
> }
>
> @@ -770,7 +782,8 @@ static int virLockManagerLockDaemonAcquire(virLockManagerPtr
lock,
> virLockManagerLockDaemonPrivatePtr priv = lock->privateData;
>
> virCheckFlags(VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY |
> - VIR_LOCK_MANAGER_ACQUIRE_RESTRICT, -1);
> + VIR_LOCK_MANAGER_ACQUIRE_RESTRICT |
> + VIR_LOCK_MANAGER_ACQUIRE_KEEP_OPEN, -1);
>
> if (priv->type == VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN &&
> priv->nresources == 0 &&
> @@ -781,7 +794,14 @@ static int virLockManagerLockDaemonAcquire(virLockManagerPtr
lock,
> return -1;
> }
>
> - if (!(client = virLockManagerLockDaemonConnect(lock, &program,
&counter)))
> + if (flags & VIR_LOCK_MANAGER_ACQUIRE_KEEP_OPEN) {
> + client = priv->client;
> + program = priv->program;
> + counter = priv->counter;
> + }
> +
> + if (!client &&
> + !(client = virLockManagerLockDaemonConnect(lock, &program,
&counter)))
If "always" storing in @priv this alters to if !priv->client &&
!(priv->client ==...
> goto cleanup;
>
> if (fd &&
> @@ -814,11 +834,25 @@ static int virLockManagerLockDaemonAcquire(virLockManagerPtr
lock,
> virLockManagerLockDaemonConnectionRestrict(lock, client, program,
&counter) < 0)
> goto cleanup;
>
> + if (flags & VIR_LOCK_MANAGER_ACQUIRE_KEEP_OPEN) {
> + VIR_STEAL_PTR(priv->client, client);
> + VIR_STEAL_PTR(priv->program, program);
> + priv->counter = counter;
> + }
> +
If "always" using @priv means this isn't necessary as long as @flags is
also stored.
The code needs to handle both cases, if the flag is set and if it isn't.
I don't want to write the code twice with the only difference being in
the variable accessed (priv->client vs client).
> rv = 0;
>
> cleanup:
> - if (rv != 0 && fd)
> - VIR_FORCE_CLOSE(*fd);
> + if (rv < 0) {
> + if (fd)
> + VIR_FORCE_CLOSE(*fd);
> +
> + priv->client = NULL;
> + priv->program = NULL;
> + priv->counter = 0;
> + priv->clientRefs = 0;
So one failure this time causes previously stored successes to be thrown
away?
Yes. The client socket is closed by code below. There's not much point
in keeping stale pointer to what used to be connection.
Or am I reading too much into this? Why is clientRefs not
auto-incremented here, but auto-decremented in Release?
Oooops. Yes. Somehow the line that increments the clientRefs counter
when grabbing the client got lost. Damn you small dwarfs who make some
lines disappear! :-D
This is where I'd think a helper would be able to know the "last"
referenced was removed and thus be able to perform the close and free of
resources.
Calling *PrivateFree and finding that there's extra clientRefs
would/could mean something is programatically wrong, right?
Yes. We don't want to free client data at this point.
> + }
> +
> virNetClientClose(client);
> virObjectUnref(client);
> virObjectUnref(program);
Always storing in @priv, priv->flags, and using @rv I would think could
easily be managed in a helper...
> @@ -837,12 +871,20 @@ static int virLockManagerLockDaemonRelease(virLockManagerPtr
lock,
> size_t i;
> virLockManagerLockDaemonPrivatePtr priv = lock->privateData;
>
> - virCheckFlags(0, -1);
> + virCheckFlags(VIR_LOCK_MANAGER_RELEASE_KEEP_OPEN, -1);
>
> if (state)
> *state = NULL;
>
> - if (!(client = virLockManagerLockDaemonConnect(lock, &program,
&counter)))
> + if (flags & VIR_LOCK_MANAGER_RELEASE_KEEP_OPEN) {
> + client = priv->client;
> + program = priv->program;
> + counter = priv->counter;
> + priv->clientRefs--;
This decrements something from 0...
> + }
> +
> + if (!client &&
> + !(client = virLockManagerLockDaemonConnect(lock, &program,
&counter)))
> goto cleanup;
>
> for (i = 0; i < priv->nresources; i++) {
> @@ -870,9 +912,23 @@ static int virLockManagerLockDaemonRelease(virLockManagerPtr
lock,
> goto cleanup;
> }
>
> + if (flags & VIR_LOCK_MANAGER_RELEASE_KEEP_OPEN) {
> + /* Avoid freeing in cleanup. */
> + client = NULL;
> + program = NULL;
> + counter = 0;
> + }
> +
> rv = 0;
>
> cleanup:
> + if (rv < 0) {
> + priv->client = NULL;
> + priv->program = NULL;
> + priv->counter = 0;
> + priv->clientRefs = 0;
> + }
> +
Again, I'm not clear why when this release fails we go off and clear out
everything? Including things that may have been connected before?
Certain a properly documented helper would solve this mystery for me.
Maybe it is too drastic. But rather to be safe then sorry. If something
goes wrong, we can't be sure what state the connection is in and it's
better for the caller to open new connection.
One that could manage things based on KEEP_OPEN, @priv, and @rv. I would
assume the close open when KEEP_OPEN is true would be where the
auto-decrement would occur and be checked to determine whether the
subsequent closes and unref's happen.
Well, do we want each caller to re-implement this semantic? I say it's
better to have it at one place.
Michal