summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-02-20 21:41:14 +0100
committerThomas Haller <thaller@redhat.com>2018-04-04 15:25:34 +0200
commitcdb55a78af3f2f2f19f47169e442c569554f6cea (patch)
tree40953ddcc4fc603d826392f400fa34f69fd3c10d
parent90606e179af0c5b4aa4c047f7c5dc023d9b02ee6 (diff)
downloadNetworkManager-th/connectivity-rework.tar.gz
connectivity: schedule connectivity timers per-device and probe for short outagesth/connectivity-rework
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
-rw-r--r--src/devices/nm-device.c344
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/main.c2
-rw-r--r--src/nm-connectivity.c33
-rw-r--r--src/nm-connectivity.h4
-rw-r--r--src/nm-core-utils.h7
-rw-r--r--src/nm-manager.c51
7 files changed, 361 insertions, 82 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 6142908a57..cc3df54ae8 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -164,6 +164,7 @@ struct _NMDeviceConnectivityHandle {
gpointer user_data;
NMConnectivityCheckHandle *c_handle;
guint64 seq;
+ bool is_periodic:1;
};
/*****************************************************************************/
@@ -543,7 +544,19 @@ typedef struct _NMDevicePrivate {
NMConnectivity *concheck_mgr;
- gulong concheck_periodic_id;
+ /* if periodic checks are enabled, this is the source id for the next check. */
+ guint concheck_p_cur_id;
+
+ /* the currently configured max periodic interval. */
+ guint concheck_p_max_interval;
+
+ /* the current interval. If we are probing, the interval might be lower
+ * then the configured max interval. */
+ guint concheck_p_cur_interval;
+
+ /* the timestamp, when we last scheduled the timer concheck_p_cur_id with current interval
+ * concheck_p_cur_interval. */
+ gint64 concheck_p_cur_basetime_ns;
NMConnectivityState connectivity_state;
@@ -2185,17 +2198,225 @@ nm_device_get_physical_port_id (NMDevice *self)
/*****************************************************************************/
+typedef enum {
+ CONCHECK_SCHEDULE_UPDATE_INTERVAL,
+ CONCHECK_SCHEDULE_CHECK_EXTERNAL,
+ CONCHECK_SCHEDULE_CHECK_PERIODIC,
+ CONCHECK_SCHEDULE_RETURNED_MIN,
+ CONCHECK_SCHEDULE_RETURNED_BUMP,
+ CONCHECK_SCHEDULE_RETURNED_MAX,
+} ConcheckScheduleMode;
+
+static NMDeviceConnectivityHandle *concheck_start (NMDevice *self,
+ NMDeviceConnectivityCallback callback,
+ gpointer user_data,
+ gboolean is_periodic);
+
+static void concheck_periodic_schedule_set (NMDevice *self,
+ ConcheckScheduleMode mode);
+
+static gboolean
+concheck_periodic_timeout_cb (gpointer user_data)
+{
+ NMDevice *self = user_data;
+
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_PERIODIC);
+ concheck_start (self, NULL, NULL, TRUE);
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+concheck_is_possible (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if ( !nm_device_is_real (self)
+ || NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_LOOPBACK))
+ return FALSE;
+
+ /* we enable periodic checks for every device state (except UNKNOWN). Especially with
+ * unmanaged devices, it is interesting to know whether we have connectivity on that device. */
+ if (priv->state == NM_DEVICE_STATE_UNKNOWN)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+concheck_periodic_schedule_do (NMDevice *self, gint64 interval_ns)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gboolean periodic_check_disabled = FALSE;
+
+ /* we always cancel whatever was pending. */
+ if (nm_clear_g_source (&priv->concheck_p_cur_id))
+ periodic_check_disabled = TRUE;
+
+ if (priv->concheck_p_max_interval == 0) {
+ /* periodic checks are disabled */
+ goto out;
+ }
+
+ nm_assert (interval_ns >= 0);
+
+ if (!concheck_is_possible (self))
+ goto out;
+
+ _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %u milliseconds (%u seconds interval)",
+ periodic_check_disabled ? "re-" : "",
+ (guint) (interval_ns / NM_UTILS_NS_PER_MSEC),
+ priv->concheck_p_cur_interval);
+
+ nm_assert (priv->concheck_p_cur_interval > 0);
+ priv->concheck_p_cur_id = g_timeout_add (interval_ns / NM_UTILS_NS_PER_MSEC,
+ concheck_periodic_timeout_cb,
+ self);
+ return TRUE;
+out:
+ if (periodic_check_disabled)
+ _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: unscheduled");
+ return FALSE;
+}
+
+#define CONCHECK_P_PROBE_INTERVAL 1
+
static void
-concheck_update_state (NMDevice *self, NMConnectivityState state)
+concheck_periodic_schedule_set (NMDevice *self,
+ ConcheckScheduleMode mode)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gint64 new_expiry, cur_expiry, tdiff;
+ gint64 now_ns = 0;
- if (state == NM_CONNECTIVITY_ERROR)
+ if (priv->concheck_p_max_interval == 0) {
+ /* periodic check is disabled. Nothing to do. */
return;
+ }
- /* If the connectivity check is disabled and we obtain a fake
- * result, make an optimistic guess. */
- if (state == NM_CONNECTIVITY_FAKE) {
+ if (!priv->concheck_p_cur_id) {
+ /* we currently don't have a timeout scheduled. No need to reschedule
+ * another one... */
+ if (mode == CONCHECK_SCHEDULE_UPDATE_INTERVAL) {
+ /* ... unless, we are initalizing. In this case, setup the current current
+ * interval and schedule a perform a check right away. */
+ priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL);
+ priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
+ if (concheck_periodic_schedule_do (self, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND))
+ concheck_start (self, NULL, NULL, TRUE);
+ }
+ return;
+ }
+
+ switch (mode) {
+ case CONCHECK_SCHEDULE_UPDATE_INTERVAL:
+ /* called with "UPDATE_INTERVAL" and already have a concheck_p_cur_id scheduled. */
+
+ if (priv->concheck_p_cur_interval <= priv->concheck_p_max_interval) {
+ /* we currently have a shorter interval set, then what we now have. Either,
+ * because we are probing, or because the previous max interval was shorter.
+ *
+ * Either way, the current timer is set just fine. Nothing to do. */
+ return;
+ }
+
+ cur_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_max_interval * NM_UTILS_NS_PER_SECOND);
+ priv->concheck_p_cur_interval = priv->concheck_p_max_interval;
+ priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
+ if (cur_expiry <= now_ns) {
+ /* the last timer was scheduled longer ago then the new desired interval. It means,
+ * we must schedule a timer right away */
+ if (concheck_periodic_schedule_do (self, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND)) {
+ concheck_start (self, NULL, NULL, TRUE);
+ }
+ } else {
+ /* we only need to reset the timer. */
+ concheck_periodic_schedule_do (self, (cur_expiry - now_ns) / NM_UTILS_NS_PER_MSEC);
+ }
+ return;
+
+ case CONCHECK_SCHEDULE_CHECK_EXTERNAL:
+ /* a external connectivity check delays our periodic check. We reset the counter. */
+ priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
+ concheck_periodic_schedule_do (self, priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
+ return;
+
+ case CONCHECK_SCHEDULE_CHECK_PERIODIC:
+ /* we schedule a periodic connectivity check now. We just remember the time when
+ * we did it. There is nothing to reschedule, it's fine already. */
+ priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns);
+ return;
+
+ /* we just got an event that we lost connectivity (that is, concheck returned). We reset
+ * the interval to min/max or increase the probe interval (bump). */
+ case CONCHECK_SCHEDULE_RETURNED_MIN:
+ priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL);
+ break;
+ case CONCHECK_SCHEDULE_RETURNED_MAX:
+ priv->concheck_p_cur_interval = priv->concheck_p_max_interval;
+ break;
+ case CONCHECK_SCHEDULE_RETURNED_BUMP:
+ priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_cur_interval * 2, priv->concheck_p_max_interval);
+ break;
+ }
+
+ /* we are here, because we returned from a connectivity check and adjust the current interval.
+ *
+ * But note that we calculate the new timeout based on the time when we scheduled the
+ * last check, instead of counting from now. The reaons is, that we want that the times
+ * when we schedule checks be at precise intervals, without including the time it took for
+ * the connectivity check. */
+ new_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND);
+ tdiff = NM_MAX (new_expiry - nm_utils_get_monotonic_timestamp_ns_cached (&now_ns), 0);
+ priv->concheck_p_cur_basetime_ns = now_ns - tdiff;
+ concheck_periodic_schedule_do (self, tdiff);
+}
+
+void
+nm_device_check_connectivity_update_interval (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ guint new_interval;
+
+ new_interval = nm_connectivity_get_interval (concheck_get_mgr (self));
+
+ new_interval = NM_MIN (new_interval, 7 *24 * 3600);
+
+ if (new_interval != priv->concheck_p_max_interval) {
+ _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: set interval to %u seconds", new_interval);
+ priv->concheck_p_max_interval = new_interval;
+ }
+
+ if (!new_interval) {
+ /* this will cancel any potentially pending timeout. */
+ concheck_periodic_schedule_do (self, 0);
+ return;
+ }
+
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_UPDATE_INTERVAL);
+}
+
+static void
+concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean is_periodic)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ /* @state is a result of the connectivity check. We only expect a precise
+ * number of possible values. */
+ nm_assert (NM_IN_SET (state, NM_CONNECTIVITY_LIMITED,
+ NM_CONNECTIVITY_PORTAL,
+ NM_CONNECTIVITY_FULL,
+ NM_CONNECTIVITY_FAKE,
+ NM_CONNECTIVITY_ERROR));
+
+ if (state == NM_CONNECTIVITY_ERROR) {
+ /* on error, we don't change the current connectivity state,
+ * except making UNKNOWN to NONE. */
+ state = priv->connectivity_state;
+ if (state == NM_CONNECTIVITY_UNKNOWN)
+ state = NM_CONNECTIVITY_NONE;
+ } else if (state == NM_CONNECTIVITY_FAKE) {
+ /* If the connectivity check is disabled and we obtain a fake
+ * result, make an optimistic guess. */
if (priv->state == NM_DEVICE_STATE_ACTIVATED) {
if (nm_device_get_best_default_route (self, AF_UNSPEC))
state = NM_CONNECTIVITY_FULL;
@@ -2205,10 +2426,32 @@ concheck_update_state (NMDevice *self, NMConnectivityState state)
state = NM_CONNECTIVITY_NONE;
}
- if (priv->connectivity_state == state)
+ if (priv->connectivity_state == state) {
+ /* we got a connectivty update, but the state didn't change. If we were probing,
+ * we bump the probe frequency. */
+ if ( is_periodic
+ && priv->concheck_p_cur_id)
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP);
return;
+ }
+ /* we need to update the probe interval before emitting signals. Emitting
+ * a signal might call back into NMDevice and change the probe settings.
+ * So, do that first. */
+ if (state == NM_CONNECTIVITY_FULL) {
+ /* we reached full connectivity state. Stop probing by setting the
+ * interval to the max. */
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MAX);
+ } else if (priv->connectivity_state == NM_CONNECTIVITY_FULL) {
+ /* we are about to loose connectivity. (re)start probing by setting
+ * the timeout interval to the min. */
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MIN);
+ } else {
+ if ( is_periodic
+ && priv->concheck_p_cur_id)
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP);
+ }
- _LOGD (LOGD_CONCHECK, "state changed from %s to %s",
+ _LOGD (LOGD_CONCHECK, "connectivity state changed from %s to %s",
nm_connectivity_state_to_string (priv->connectivity_state),
nm_connectivity_state_to_string (state));
priv->connectivity_state = state;
@@ -2227,34 +2470,6 @@ concheck_update_state (NMDevice *self, NMConnectivityState state)
}
static void
-concheck_periodic (NMConnectivity *connectivity, NMDevice *self)
-{
- nm_device_check_connectivity (self, NULL, NULL);
-}
-
-static void
-concheck_periodic_update (NMDevice *self)
-{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
- if ( priv->state == NM_DEVICE_STATE_ACTIVATED
- && nm_device_get_best_default_route (self, AF_UNSPEC)) {
- if (!priv->concheck_periodic_id) {
- priv->concheck_periodic_id = g_signal_connect (concheck_get_mgr (self),
- NM_CONNECTIVITY_PERIODIC_CHECK,
- G_CALLBACK (concheck_periodic), self);
- nm_device_check_connectivity (self, NULL, NULL);
- }
- } else {
- if (priv->concheck_periodic_id) {
- /* The default route has gone off, trigger a final connectivity check. */
- nm_clear_g_signal_handler (priv->concheck_mgr, &priv->concheck_periodic_id);
- nm_device_check_connectivity (self, NULL, NULL);
- }
- }
-}
-
-static void
concheck_handle_complete (NMDeviceConnectivityHandle *handle,
GError *error)
{
@@ -2264,7 +2479,7 @@ concheck_handle_complete (NMDeviceConnectivityHandle *handle,
c_list_unlink (&handle->concheck_lst);
if (handle->c_handle)
- nm_connectivity_check_cancel (g_steal_pointer (&handle->c_handle));
+ nm_connectivity_check_cancel (handle->c_handle);
if (handle->callback) {
handle->callback (handle->self,
@@ -2291,6 +2506,18 @@ concheck_cb (NMConnectivity *connectivity,
gboolean handle_is_alive;
guint64 seq;
+ handle = user_data;
+ nm_assert (handle->c_handle == c_handle);
+ nm_assert (NM_IS_DEVICE (handle->self));
+
+ handle->c_handle = NULL;
+ self = g_object_ref (handle->self);
+
+ _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s%s%s%s)",
+ (long long unsigned) handle->seq,
+ nm_connectivity_state_to_string (state),
+ NM_PRINT_FMT_QUOTED (error, ", error: ", error->message, "", ""));
+
if (nm_utils_error_is_cancelled (error, FALSE)) {
/* the only place where we nm_connectivity_check_cancel(@c_handle), is
* from inside concheck_handle_event(). This is a recursive call,
@@ -2301,12 +2528,7 @@ concheck_cb (NMConnectivity *connectivity,
/* we keep NMConnectivity instance alive. It cannot be disposing. */
nm_assert (!nm_utils_error_is_cancelled (error, TRUE));
- handle = user_data;
- nm_assert (handle->c_handle == c_handle);
- handle->c_handle = NULL;
-
/* keep @self alive, while we invoke callbacks. */
- self = g_object_ref (handle->self);
priv = NM_DEVICE_GET_PRIVATE (self);
nm_assert (!handle || c_list_contains (&priv->concheck_lst_head, &handle->concheck_lst));
@@ -2314,7 +2536,7 @@ concheck_cb (NMConnectivity *connectivity,
seq = handle->seq;
/* first update the new state, and emit signals. */
- concheck_update_state (self, state);
+ concheck_update_state (self, state, handle->is_periodic);
handle_is_alive = FALSE;
@@ -2361,10 +2583,11 @@ check_handles:
concheck_handle_complete (handle, NULL);
}
-NMDeviceConnectivityHandle *
-nm_device_check_connectivity (NMDevice *self,
- NMDeviceConnectivityCallback callback,
- gpointer user_data)
+static NMDeviceConnectivityHandle *
+concheck_start (NMDevice *self,
+ NMDeviceConnectivityCallback callback,
+ gpointer user_data,
+ gboolean is_periodic)
{
static guint64 seq_counter = 0;
NMDevicePrivate *priv;
@@ -2379,8 +2602,14 @@ nm_device_check_connectivity (NMDevice *self,
handle->self = self;
handle->callback = callback;
handle->user_data = user_data;
+ handle->is_periodic = is_periodic;
+
c_list_link_tail (&priv->concheck_lst_head, &handle->concheck_lst);
+ _LOGT (LOGD_CONCHECK, "connectivity: start check (seq:%llu%s)",
+ (long long unsigned) handle->seq,
+ is_periodic ? ", periodic-check" : "");
+
handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self),
nm_device_get_ip_iface (self),
concheck_cb,
@@ -2388,6 +2617,21 @@ nm_device_check_connectivity (NMDevice *self,
return handle;
}
+NMDeviceConnectivityHandle *
+nm_device_check_connectivity (NMDevice *self,
+ NMDeviceConnectivityCallback callback,
+ gpointer user_data)
+{
+ NMDeviceConnectivityHandle *handle;
+
+ if (!concheck_is_possible (self))
+ return NULL;
+
+ concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_EXTERNAL);
+ handle = concheck_start (self, callback, user_data, FALSE);
+ return handle;
+}
+
void
nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle)
{
@@ -10688,8 +10932,6 @@ nm_device_set_ip_config (NMDevice *self,
}
if (IS_IPv4) {
- concheck_periodic_update (self);
-
if (!nm_device_sys_iface_state_is_external_or_assume (self))
ip4_rp_filter_update (self);
}
@@ -13544,7 +13786,7 @@ _set_state_full (NMDevice *self,
if (ip_config_valid (old_state) && !ip_config_valid (state))
notify_ip_properties (self);
- concheck_periodic_update (self);
+ nm_device_check_connectivity_update_interval (self);
/* Dispose of the cached activation request */
if (req)
@@ -14711,7 +14953,7 @@ dispose (GObject *object)
g_clear_object (&priv->lldp_listener);
}
- nm_clear_g_signal_handler (priv->concheck_mgr, &priv->concheck_periodic_id);
+ nm_clear_g_source (&priv->concheck_p_cur_id);
G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 24d184657f..fd266d69ab 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -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);
diff --git a/src/main.c b/src/main.c
index 69902c8437..bd8e26a556 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 039ff71d05..d46b7d2859 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -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,
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index 6c16982307..df9295e02b 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -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,
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index d974c8af36..33773ab679 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -239,6 +239,13 @@ gint64 nm_utils_get_monotonic_timestamp_ms (void);
gint32 nm_utils_get_monotonic_timestamp_s (void);
gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns);
+static inline gint64
+nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now)
+{
+ return (*cache_now)
+ ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ());
+}
+
gboolean nm_utils_is_valid_path_component (const char *name);
const char *NM_ASSERT_VALID_PATH_COMPONENT (const char *name);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 872708337d..f8e4580e80 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -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;
@@ -333,6 +333,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;
@@ -5504,10 +5532,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) {
@@ -6624,7 +6652,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);
@@ -6754,6 +6782,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),