Commit 0a62a0e9 authored by Thomas Haller's avatar Thomas Haller

connectivity: schedule connectivity timers per-device and probe for short outages

It might happen, that connectivitiy is lost only for a moment and
returns soon after. Based on that assumption, when we loose connectivity
we want to have a probe interval where we check for returning
connectivity more frequently.

For that, we handle tracking of the timeouts per-device.

The intervall shall start with 1 seconds, and double the interval time until
the full interval is reached. Actually, due to the implementation, it's unlikely
that we already perform the second check 1 second later. That is because commonly
the first check returns before the one second timeout is reached and bumps the
interval to 2 seconds right away.

Also, we go through extra lengths so that manual connectivity check
delay the periodic checks. By being more smart about that, we can reduce
the number of connectivity checks, but still keeping the promise to
check at least within the requested interval.

The complexity of book keeping the timeouts is remarkable. But I think
it is worth the effort and we should try hard to

 - have a connectivity state as accurate as possible. Clearly,
   connectivity checking means that we probing, so being more intelligent
   about timeout and backoff timers can result in a better connectivity
   state. The connectivity state is important because we use it for
   the default-route penaly and the GUI indicates bad connectivity.

 - be intelligent about avoiding redundant connectivity checks. While
   we want to check often to get an accurate connectivity state, we
   also want to minimize the number of HTTP requests, in case the
   connectivity is established and suppossedly stable.

Also, perform connectivity checks in every state of the device.
Even if a device is disconnected, it still might have connectivity,
for example if the user externally adds an IP address on an unmanaged
device.

https://bugzilla.gnome.org/show_bug.cgi?id=792240
parent e8e0ef63
This diff is collapsed.
......@@ -783,6 +783,8 @@ typedef void (*NMDeviceConnectivityCallback) (NMDevice *self,
GError *error,
gpointer user_data);
void nm_device_check_connectivity_update_interval (NMDevice *self);
NMDeviceConnectivityHandle *nm_device_check_connectivity (NMDevice *self,
NMDeviceConnectivityCallback callback,
gpointer user_data);
......
......@@ -402,8 +402,6 @@ main (int argc, char *argv[])
manager))
goto done;
NM_UTILS_KEEP_ALIVE (manager, nm_connectivity_get (), "NMManager-depends-on-NMConnectivity");
nm_dispatcher_init ();
g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
......
......@@ -81,7 +81,7 @@ struct _NMConnectivityCheckHandle {
};
enum {
PERIODIC_CHECK,
CONFIG_CHANGED,
LAST_SIGNAL
};
......@@ -99,7 +99,6 @@ typedef struct {
struct {
CURLM *curl_mhandle;
guint curl_timer;
guint periodic_check_id;
} concheck;
#endif
} NMConnectivityPrivate;
......@@ -539,21 +538,20 @@ nm_connectivity_check_cancel (NMConnectivityCheckHandle *cb_data)
gboolean
nm_connectivity_check_enabled (NMConnectivity *self)
{
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
g_return_val_if_fail (NM_IS_CONNECTIVITY (self), FALSE);
return priv->enabled;
return NM_CONNECTIVITY_GET_PRIVATE (self)->enabled;
}
/*****************************************************************************/
#if WITH_CONCHECK
static gboolean
periodic_check (gpointer user_data)
guint
nm_connectivity_get_interval (NMConnectivity *self)
{
g_signal_emit (NM_CONNECTIVITY (user_data), signals[PERIODIC_CHECK], 0);
return G_SOURCE_CONTINUE;
return nm_connectivity_check_enabled (self)
? NM_CONNECTIVITY_GET_PRIVATE (self)->interval
: 0;
}
#endif
static void
update_config (NMConnectivity *self, NMConfigData *config_data)
......@@ -592,6 +590,7 @@ update_config (NMConnectivity *self, NMConfigData *config_data)
/* Set the interval. */
interval = nm_config_data_get_connectivity_interval (config_data);
interval = MIN (interval, (7 * 24 * 3600));
if (priv->interval != interval) {
priv->interval = interval;
changed = TRUE;
......@@ -622,13 +621,8 @@ update_config (NMConnectivity *self, NMConfigData *config_data)
changed = TRUE;
}
#if WITH_CONCHECK
if (changed) {
nm_clear_g_source (&priv->concheck.periodic_check_id);
if (nm_connectivity_check_enabled (self))
priv->concheck.periodic_check_id = g_timeout_add_seconds (priv->interval, periodic_check, self);
}
#endif
if (changed)
g_signal_emit (self, signals[CONFIG_CHANGED], 0);
}
static void
......@@ -699,7 +693,6 @@ again:
curl_multi_cleanup (priv->concheck.curl_mhandle);
curl_global_cleanup ();
nm_clear_g_source (&priv->concheck.periodic_check_id);
#endif
if (priv->config) {
......@@ -715,8 +708,8 @@ nm_connectivity_class_init (NMConnectivityClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
signals[PERIODIC_CHECK] =
g_signal_new (NM_CONNECTIVITY_PERIODIC_CHECK,
signals[CONFIG_CHANGED] =
g_signal_new (NM_CONNECTIVITY_CONFIG_CHANGED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL,
......
......@@ -34,7 +34,7 @@
#define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTIVITY))
#define NM_CONNECTIVITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
#define NM_CONNECTIVITY_PERIODIC_CHECK "nm-connectivity-periodic-check"
#define NM_CONNECTIVITY_CONFIG_CHANGED "config-changed"
typedef struct _NMConnectivityClass NMConnectivityClass;
......@@ -46,6 +46,8 @@ const char *nm_connectivity_state_to_string (NMConnectivityState state);
gboolean nm_connectivity_check_enabled (NMConnectivity *self);
guint nm_connectivity_get_interval (NMConnectivity *self);
typedef struct _NMConnectivityCheckHandle NMConnectivityCheckHandle;
typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self,
......
......@@ -132,10 +132,8 @@ typedef struct {
NMState state;
NMConfig *config;
NMConnectivityState connectivity_state;
NMConnectivity *concheck_mgr;
NMPolicy *policy;
NMHostnameManager *hostname_manager;
struct {
......@@ -170,6 +168,8 @@ typedef struct {
guint devices_inited_id;
NMConnectivityState connectivity_state;
bool startup:1;
bool devices_inited:1;
......@@ -334,6 +334,34 @@ static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
/*****************************************************************************/
static void
concheck_config_changed_cb (NMConnectivity *connectivity,
NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMDevice *device;
c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
nm_device_check_connectivity_update_interval (device);
}
static NMConnectivity *
concheck_get_mgr (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
if (G_UNLIKELY (!priv->concheck_mgr)) {
priv->concheck_mgr = g_object_ref (nm_connectivity_get ());
g_signal_connect (priv->concheck_mgr,
NM_CONNECTIVITY_CONFIG_CHANGED,
G_CALLBACK (concheck_config_changed_cb),
self);
}
return priv->concheck_mgr;
}
/*****************************************************************************/
typedef struct {
int ifindex;
guint32 aspired_metric;
......@@ -5527,10 +5555,10 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
data->remaining = 0;
c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
data->remaining++;
nm_device_check_connectivity (device,
device_connectivity_done,
data);
if (nm_device_check_connectivity (device,
device_connectivity_done,
data))
data->remaining++;
}
if (data->remaining == 0) {
......@@ -6647,7 +6675,7 @@ get_property (GObject *object, guint prop_id,
break;
case PROP_CONNECTIVITY_CHECK_ENABLED:
g_value_set_boolean (value,
nm_connectivity_check_enabled (nm_connectivity_get ()));
nm_connectivity_check_enabled (concheck_get_mgr (self)));
break;
case PROP_PRIMARY_CONNECTION:
nm_dbus_utils_g_value_set_object_path (value, priv->primary_connection);
......@@ -6777,6 +6805,13 @@ dispose (GObject *object)
g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_free);
if (priv->concheck_mgr) {
g_signal_handlers_disconnect_by_func (priv->concheck_mgr,
G_CALLBACK (concheck_config_changed_cb),
self);
g_clear_object (&priv->concheck_mgr);
}
if (priv->auth_mgr) {
g_signal_handlers_disconnect_by_func (priv->auth_mgr,
G_CALLBACK (auth_mgr_changed),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment