/*********************************************************
- * Copyright (C) 2008-2016,2018-2019 VMware, Inc. All rights reserved.
+ * Copyright (C) 2008-2016,2018-2020 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
/**
- * Starts the RpcIn loop and the RpcOut channel.
+ * Starts the RpcOut channel.
*
- * No-op if channels are already started.
+ * No-op if channels are already started. If RpcIn
+ * channel is needed, it must have been started
+ * before calling this function.
+ *
+ * In case of error, stops RpcIn.
*
* @param[in] chan The RPC channel instance.
*
}
-/**
- * Shuts down the RpcIn channel. Due to the "split brain" nature of the backdoor,
- * if this function fails, it's possible that while the "out" channel was shut
- * down the "in" one wasn't, for example, although that's unlikely.
- *
- * @param[in] chan The RPC channel instance.
- */
-
-static void
-BkdoorChannelShutdown(RpcChannel *chan)
-{
- BackdoorChannel *bdoor = chan->_private;
- BkdoorChannelStop(chan);
- RpcOut_Destruct(bdoor->out);
- g_free(bdoor);
- chan->_private = NULL;
-}
-
-
/**
* Sends the data using the RpcOut library.
*
}
+/**
+ * Callback function to destroy the Backdoor channel after
+ * it fails to start or it has been stopped.
+ *
+ * @param[in] chan The RPC channel instance.
+ */
+
+static void
+BkdoorChannelDestroy(RpcChannel *chan)
+{
+ BackdoorChannel *bdoor = chan->_private;
+
+ /*
+ * Channel should be stopped before destroying it.
+ */
+ ASSERT(!chan->outStarted);
+ RpcOut_Destruct(bdoor->out);
+ g_free(bdoor);
+ chan->_private = NULL;
+}
+
+
+/**
+ * Shuts down the Backdoor RpcOut channel.
+ *
+ * Due to the "split brain" nature of the backdoor, if this function
+ * fails, it's possible that while the "out" channel was shut down
+ * the "in" one wasn't, for example, although that's unlikely.
+ *
+ * @param[in] chan The RPC channel instance.
+ */
+
+static void
+BkdoorChannelShutdown(RpcChannel *chan)
+{
+ BkdoorChannelStop(chan);
+ BkdoorChannelDestroy(chan);
+}
+
+
/**
* Return the channel type.
*
* @param[in] chan RpcChannel
*
- * @return backdoor channel type.
+ * @return RpcChannelType.
*/
static RpcChannelType
NULL,
BkdoorChannelShutdown,
BkdoorChannelGetType,
- NULL,
- NULL
+ BkdoorChannelDestroy
};
ASSERT(chan);
ret->inStarted = FALSE;
#endif
ret->outStarted = FALSE;
+ /*
+ * Backdoor channel is not mutable as it has no
+ * fallback option available.
+ */
+ ret->isMutable = FALSE;
BackdoorChannelSetCallbacks(ret);
ret->_private = bdoor;
* Fall back to backdoor when another type of RpcChannel fails to start.
*
* @param[in] chan RpcChannel
- *
- * @return TRUE on success.
*/
-gboolean
+void
BackdoorChannel_Fallback(RpcChannel *chan)
{
BackdoorChannel *bdoor;
BackdoorChannelSetCallbacks(chan);
chan->_private = bdoor;
-
- return chan->funcs->start(chan);
}
-
static gboolean gUseBackdoorOnly = FALSE;
/*
- * Track the vSocket connection failure, so that we can
- * avoid using vSockets until a channel reset/restart or
- * the service itself gets restarted.
+ * Delay in seconds before retrying vSocket after
+ * falling back to Backdoor (min=2sec, max=5min).
+ *
+ * Trying vSocket when it is not working can lead to
+ * futile attempts with almost each attempt taking 2s
+ * to timeout. To avoid this delay, don't attempt to use
+ * vSocket for a while.
*/
-static gboolean gVSocketFailed = FALSE;
+#define RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY (2)
+#define RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY (5 * 60)
+
static void RpcChannelStopNoLock(RpcChannel *chan);
RpcChannelStopNoLock(&chan->impl);
- /* Clear vSocket channel failure */
- Log(LGPFX "Clearing backdoor behavior ...\n");
- gVSocketFailed = FALSE;
+ if (chan->impl.vsockFailureTS != 0) {
+ /* Clear vSocket channel failure */
+ Log(LGPFX "Clearing backdoor behavior ...\n");
+ chan->impl.vsockFailureTS = 0;
+ chan->impl.vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
+ }
chanStarted = RpcChannel_Start(&chan->impl);
g_mutex_unlock(&chan->impl.outLock);
/* Reset was successful. */
Log(LGPFX "Channel was reset successfully.\n");
chan->rpcResetErrorCount = 0;
- Log(LGPFX "Clearing backdoor behavior ...\n");
- gVSocketFailed = FALSE;
+
+ if (chan->impl.vsockFailureTS != 0) {
+ Log(LGPFX "Clearing backdoor behavior ...\n");
+ chan->impl.vsockFailureTS = 0;
+ chan->impl.vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
+ }
if (chan->resetCb != NULL) {
chan->resetCb(&chan->impl, TRUE, chan->resetData);
{
RpcChannelInt *chan = _chan;
- Debug(LGPFX " %s: Clearing cumulative RpcChannel error count; was %d\n",
- __FUNCTION__, chan->rpcFailureCount);
+ Debug(LGPFX "Clearing cumulative RpcChannel error count; was %d\n",
+ chan->rpcFailureCount);
chan->rpcFailureCount = 0;
}
RpcChannel_Create(void)
{
RpcChannelInt *chan = g_new0(RpcChannelInt, 1);
+ chan->impl.vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
return &chan->impl;
}
{
RpcChannel *chan;
#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
- chan = (gUseBackdoorOnly || gVSocketFailed) ?
- BackdoorChannel_New() : VSockChannel_New(flags);
+ chan = gUseBackdoorOnly ? BackdoorChannel_New() : VSockChannel_New(flags);
#else
chan = BackdoorChannel_New();
#endif
#endif
funcs = chan->funcs;
+
+#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
+ if (!gUseBackdoorOnly && chan->isMutable &&
+ funcs->getType(chan) == RPCCHANNEL_TYPE_BKDOOR) {
+ /*
+ * Try vsocket first for mutable channel.
+ * Existing channel needs to be destroyed before switching.
+ */
+ Log(LGPFX "Restore vsocket RpcOut channel ...\n");
+ funcs->destroy(chan);
+ VSockChannel_Restore(chan, chan->vsockChannelFlags);
+ funcs = chan->funcs;
+ }
+#endif
+
ok = funcs->start(chan);
- if (!ok && funcs->onStartErr != NULL) {
- Log(LGPFX "Fallback to backdoor ...\n");
- funcs->onStartErr(chan);
- ok = BackdoorChannel_Fallback(chan);
+ /*
+ * Try to fallback to Backdoor channel if the failed
+ * channel is mutable and is not a Backdoor channel.
+ */
+ if (!ok && chan->isMutable &&
+ funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) {
+ Log(LGPFX "Fallback to backdoor RpcOut channel ...\n");
+ funcs->destroy(chan);
+ BackdoorChannel_Fallback(chan);
+ funcs = chan->funcs;
+ ok = funcs->start(chan);
+
/*
* As vSocket is not available, we stick the backdoor
- * behavior until the channel is reset/restarted.
+ * behavior until the channel is reset/restarted or
+ * retry delay has passed.
+ */
+ chan->vsockFailureTS = time(NULL);
+ /*
+ * Backoff retry attempts. Cap the delay at max value.
*/
- Log(LGPFX "Sticking backdoor behavior ...\n");
- gVSocketFailed = TRUE;
+ chan->vsockRetryDelay *= 2;
+ if (chan->vsockRetryDelay > RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY) {
+ chan->vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY;
+ }
+ Log(LGPFX "Sticking backdoor RpcOut channel for %u seconds.\n",
+ chan->vsockRetryDelay);
}
return ok;
*resultLen = 0;
}
+#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
+ if (chan->isMutable &&
+ funcs->getType(chan) == RPCCHANNEL_TYPE_BKDOOR) {
+ /*
+ * Switch the channel type if it has been long enough
+ * time since last vsocket failure.
+ */
+ gboolean tryVSocket = (chan->vsockFailureTS == 0 ||
+ (time(NULL) - chan->vsockFailureTS) >=
+ chan->vsockRetryDelay);
+ if (tryVSocket && funcs->stop != NULL) {
+ Log(LGPFX "Stop backdoor RpcOut channel and try vsock again ...\n");
+ /*
+ * Stop existing RpcOut channel and start it again.
+ * RpcChannel_Start will switch it to vsocket when
+ * possible or fallback to Backdoor again.
+ */
+ funcs->stop(chan);
+ if (!RpcChannel_Start(chan)) {
+ ok = FALSE;
+ goto exit;
+ }
+ funcs = chan->funcs;
+ ASSERT(funcs->send);
+ }
+ }
+#endif
+
ok = funcs->send(chan, data, dataLen, &rpcStatus, &res, &resLen);
if (!ok && (funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) &&
- (funcs->stopRpcOut != NULL)) {
+ (funcs->stop != NULL)) {
free(res);
res = NULL;
resLen = 0;
/* retry once */
- Log(LGPFX "Stop RpcOut channel and try to send again ...\n");
- funcs->stopRpcOut(chan);
+ Log(LGPFX "Stop vsock RpcOut channel and try to send again ...\n");
+ funcs->stop(chan);
+ /*
+ * This is first send failure on vsocket RpcOut channel.
+ * So, we re-init the failure timestamp and retry delay
+ * because RpcChannel_Start tries vsocket first. In case
+ * RpcChannel_Start falls back to Backdoor these will be
+ * set appropriately.
+ */
+ chan->vsockFailureTS = 0;
+ chan->vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
if (RpcChannel_Start(chan)) {
/* The channel may get switched from vsocket to backdoor */
funcs = chan->funcs;
const char *appName, gpointer appCtx);
void (*shutdown)(RpcChannel *);
RpcChannelType (*getType)(RpcChannel *chan);
- void (*onStartErr)(RpcChannel *);
- gboolean (*stopRpcOut)(RpcChannel *);
+ void (*destroy)(RpcChannel *);
} RpcChannelFuncs;
/**
* channels, their state (inStarted/outStarted) and _private data.
*/
struct _RpcChannel {
- const RpcChannelFuncs *funcs;
- gpointer _private;
+ const RpcChannelFuncs *funcs;
+ gpointer _private;
#if defined(NEED_RPCIN)
- GMainContext *mainCtx;
- const char *appName;
- gpointer appCtx;
+ GMainContext *mainCtx;
+ const char *appName;
+ gpointer appCtx;
+ struct RpcIn *in;
+ gboolean inStarted;
#endif
- GMutex outLock;
-#if defined(NEED_RPCIN)
- struct RpcIn *in;
- gboolean inStarted;
-#endif
- gboolean outStarted;
+ GMutex outLock;
+ gboolean outStarted;
+ int vsockChannelFlags;
+ /*
+ * Only vsocket channel is mutable as it can fallback to Backdoor.
+ * If a channel is created as Backdoor, it will not be mutable.
+ */
+ gboolean isMutable;
+ /*
+ * Track the last vsocket connection failure timestamp.
+ * Avoid using vsocket until a channel reset/restart
+ * occurs, the service restarts or retry delay has passed
+ * since the last failure.
+ */
+ uint64 vsockFailureTS;
+ /*
+ * Amount of time to delay next vsocket retry attempt.
+ * It varies between RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY
+ * and RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY.
+ */
+ uint32 vsockRetryDelay;
};
-gboolean
-BackdoorChannel_Fallback(RpcChannel *chan);
+void BackdoorChannel_Fallback(RpcChannel *chan);
+void VSockChannel_Restore(RpcChannel *chan, int flags);
#endif /* _RPCCHANNELINT_H_ */
-
*
* VSockOutStart --
*
- * Open the channel
+ * Start the VSockOut channel by creating a vsocket connection.
*
* Result:
* TRUE on success
*
* VSockOutStop --
*
- * Close the channel
+ * Close the underlying vsocket for the VSockOut channel
*
* Result
* None
/*
*-----------------------------------------------------------------------------
*
- * VSockChannelOnStartErr --
+ * VSockChannelDestroy --
*
- * Callback function to cleanup after channel start failure.
+ * Callback function to destroy the VSockChannel after it fails to start
+ * or it has been stopped.
*
* Results:
* None.
*/
static void
-VSockChannelOnStartErr(RpcChannel *chan) // IN
+VSockChannelDestroy(RpcChannel *chan) // IN
{
VSockChannel *vsock = chan->_private;
+ /*
+ * Channel should be stopped before destroying it.
+ */
+ ASSERT(!chan->outStarted);
VSockOutDestruct(vsock->out);
g_free(vsock);
chan->_private = NULL;
*
* VSockChannelStart --
*
- * Starts the RpcIn loop and the VSockOut channel.
+ * Starts the VSockOut channel.
*
* Results:
* TRUE on success.
*
* VSockChannelShutdown --
*
- * Shuts down the Rpc channel.
+ * Shuts down the VSockChannel.
*
* Results:
* None.
static void
VSockChannelShutdown(RpcChannel *chan) // IN
{
- VSockChannel *vsock = chan->_private;
-
VSockChannelStop(chan);
- VSockOutDestruct(vsock->out);
- g_free(vsock);
- chan->_private = NULL;
+ VSockChannelDestroy(chan);
}
/*
*-----------------------------------------------------------------------------
*
- * VSockChannel_GetType --
+ * VSockChannelGetType --
*
- * Return the channel type that being used.
+ * Return the channel type that is being used.
*
* Result:
- * return the channel type.
+ * return RpcChannelType
*
* Side-effects:
* None
*/
static RpcChannelType
-VSockChannelGetType(RpcChannel *chan)
+VSockChannelGetType(RpcChannel *chan) // IN
{
VSockChannel *vsock = chan->_private;
/*
*-----------------------------------------------------------------------------
*
- * VSockChannelStopRpcOut --
+ * VSockChannelSetCallbacks --
*
- * Stop the RpcOut channel
+ * Helper function to setup RpcChannel callbacks.
*
* Result:
- * return TRUE on success.
+ * None
*
* Side-effects:
* None
*-----------------------------------------------------------------------------
*/
-static gboolean
-VSockChannelStopRpcOut(RpcChannel *chan)
+static void
+VSockChannelSetCallbacks(RpcChannel *chan) // IN
{
- VSockChannel *vsock = chan->_private;
- VSockOutStop(vsock->out);
- chan->outStarted = FALSE;
+ static RpcChannelFuncs funcs = {
+ VSockChannelStart,
+ VSockChannelStop,
+ VSockChannelSend,
+ NULL,
+ VSockChannelShutdown,
+ VSockChannelGetType,
+ VSockChannelDestroy
+ };
- return TRUE;
+ ASSERT(chan);
+ chan->funcs = &funcs;
}
-
-
/*
*-----------------------------------------------------------------------------
*
* VSockChannel_New --
*
- * Creates a new RpcChannel channel that uses the vsocket for
+ * Creates a new RpcChannel that uses the vsocket for
* communication.
*
* Result:
*/
RpcChannel *
-VSockChannel_New(int flags)
+VSockChannel_New(int flags) // IN
{
RpcChannel *chan;
VSockChannel *vsock;
- static RpcChannelFuncs funcs = {
- VSockChannelStart,
- VSockChannelStop,
- VSockChannelSend,
- NULL,
- VSockChannelShutdown,
- VSockChannelGetType,
- VSockChannelOnStartErr,
- VSockChannelStopRpcOut
- };
-
chan = RpcChannel_Create();
vsock = g_malloc0(sizeof *vsock);
chan->inStarted = FALSE;
#endif
chan->outStarted = FALSE;
+ chan->vsockChannelFlags = flags;
+ /*
+ * VSock channel is mutable, it can fallback/change to Backdoor.
+ */
+ chan->isMutable = TRUE;
+ VSockChannelSetCallbacks(chan);
chan->_private = vsock;
- chan->funcs = &funcs;
g_mutex_init(&chan->outLock);
return chan;
}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VSockChannel_Restore --
+ *
+ * Restores RpcChannel as VSockChannel.
+ *
+ * Result:
+ * None
+ *
+ * Side-effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+VSockChannel_Restore(RpcChannel *chan, // IN
+ int flags) // IN
+{
+ VSockChannel *vsock;
+
+ ASSERT(chan);
+ ASSERT(chan->_private == NULL);
+
+ vsock = g_malloc0(sizeof *vsock);
+ vsock->out = VSockOutConstruct(flags);
+ ASSERT(vsock->out != NULL);
+
+ VSockChannelSetCallbacks(chan);
+ chan->_private = vsock;
+}
/*********************************************************
- * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
+ * Copyright (C) 2010-2020 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
data->handler.addsTimestamp = TRUE;
data->handler.shared = TRUE;
data->handler.dtor = VMXLoggerDestroy;
- data->chan = RpcChannel_New();
+ data->chan = BackdoorChannel_New();
return &data->handler;
}
/*********************************************************
- * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
+ * Copyright (C) 2008-2016,2020 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
RpcDebugSetup,
RpcDebugShutdown,
NULL,
- NULL,
NULL
};