From f7897daeb53be6001ffef58addc40f55370cfe59 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 7 Jul 2025 10:51:37 -0500 Subject: [PATCH 1/3] broadband-modem,iface-modem: move bearer counting to interface and generalize Allow counting more kinds of bearers than just multiplexed ones. Signed-off-by: Dan Williams --- src/mm-bearer-mbim.c | 9 +++--- src/mm-bearer-qmi.c | 9 +++--- src/mm-broadband-modem.c | 46 --------------------------- src/mm-broadband-modem.h | 6 ---- src/mm-iface-modem.c | 68 ++++++++++++++++++++++++++++++++++++++++ src/mm-iface-modem.h | 15 +++++++++ 6 files changed, 93 insertions(+), 60 deletions(-) diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c index eb30f78a..d102f33b 100644 --- a/src/mm-bearer-mbim.c +++ b/src/mm-bearer-mbim.c @@ -1459,10 +1459,11 @@ load_settings_from_bearer (MMBearerMbim *self, guint max_multiplexed_bearers; const gchar *data_port_driver; - if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), - ¤t_multiplexed_bearers, - &max_multiplexed_bearers, - error)) + if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem), + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED, + ¤t_multiplexed_bearers, + &max_multiplexed_bearers, + error)) return FALSE; /* Check multiplex support in the kernel and the device */ diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c index 8f387ce8..64d983eb 100644 --- a/src/mm-bearer-qmi.c +++ b/src/mm-bearer-qmi.c @@ -2425,10 +2425,11 @@ load_settings_from_bearer (MMBearerQmi *self, guint max_multiplexed_bearers; gboolean multiplex_supported = TRUE; - if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), - ¤t_multiplexed_bearers, - &max_multiplexed_bearers, - error)) + if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem), + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED, + ¤t_multiplexed_bearers, + &max_multiplexed_bearers, + error)) return FALSE; /* Check multiplex support in the kernel and the device */ diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 9a83ec05..b36f4896 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -13809,52 +13809,6 @@ mm_broadband_modem_get_current_charset (MMBroadbandModem *self) /*****************************************************************************/ -static void -bearer_count_multiplexed_connected (MMBaseBearer *bearer, - guint *count) -{ - /* The Multiplexed property is only set if connected, so it's enough to check - * that one to see if we're connected and multiplexed */ - if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer))) - *count += 1; -} - -gboolean -mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self, - guint *out_current, - guint *out_max, - GError **error) -{ - g_autoptr(MMBearerList) list = NULL; - guint max; - guint count = 0; - - g_object_get (self, - MM_IFACE_MODEM_BEARER_LIST, &list, - NULL); - - if (!list) { - g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable"); - return FALSE; - } - - max = mm_bearer_list_get_max_active_multiplexed (list); - - mm_bearer_list_foreach (list, - (MMBearerListForeachFunc)bearer_count_multiplexed_connected, - &count); - g_assert (!(!max && count)); - - if (out_max) - *out_max = max; - if (out_current) - *out_current = count; - - return TRUE; -} - -/*****************************************************************************/ - gchar * mm_broadband_modem_create_device_identifier (MMBroadbandModem *self, const gchar *ati, diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h index b96e64e3..366b33dc 100644 --- a/src/mm-broadband-modem.h +++ b/src/mm-broadband-modem.h @@ -135,12 +135,6 @@ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem GError **error); void mm_broadband_modem_sim_hot_swap_ports_context_reset (MMBroadbandModem *self); -/* Helper to manage multiplexed bearers */ -gboolean mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self, - guint *out_current, - guint *out_max, - GError **error); - /* Helper to manage initial EPS bearer */ gint mm_broadband_modem_get_initial_eps_bearer_cid (MMBroadbandModem *self); diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index 0e14d77c..af37ca2b 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -6784,6 +6784,74 @@ mm_iface_modem_get_current_modes (MMIfaceModem *self, /*****************************************************************************/ +typedef struct { + const MMIfaceModemCountBearersFlags flags; + guint count; +} CountBearersContext; + +static void +bearer_count (MMBaseBearer *bearer, + CountBearersContext *ctx) +{ + MMBearerStatus status; + + status = mm_base_bearer_get_status (MM_BASE_BEARER (bearer)); + + if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED) { + /* The Multiplexed property is only set if connected, so it's enough to check + * that one to see if we're connected and multiplexed */ + if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer))) + ctx->count++; + } else if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_CONNECTED) { + if (status == MM_BEARER_STATUS_CONNECTED) + ctx->count++; + } else if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE) { + if (status >= MM_BEARER_STATUS_CONNECTING) + ctx->count++; + } else + ctx->count++; +} + +gboolean +mm_iface_modem_count_bearers (MMIfaceModem *self, + MMIfaceModemCountBearersFlags flags, + guint *out_current, + guint *out_max, + GError **error) +{ + g_autoptr(MMBearerList) list = NULL; + guint max = 0; + CountBearersContext ctx = { + .flags = flags, + .count = 0, + }; + + g_object_get (self, + MM_IFACE_MODEM_BEARER_LIST, &list, + NULL); + if (!list) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable"); + return FALSE; + } + + if (flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED) + max = mm_bearer_list_get_max_active_multiplexed (list); + else + max = mm_bearer_list_get_max_active (list); + + mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_count, &ctx); + g_assert (max >= ctx.count); + + if (out_max) + *out_max = max; + if (out_current) + *out_current = ctx.count; + + return TRUE; +} + +/*****************************************************************************/ + static void mm_iface_modem_default_init (MMIfaceModemInterface *iface) { diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 4bd6ac19..eab98d1a 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -622,4 +622,19 @@ void mm_iface_modem_process_sim_event (MMIfaceModem *self); gboolean mm_iface_modem_is_primary_sim_slot_switch_ongoing (MMIfaceModem *self); +typedef enum { + /* Connecting or connected bearers */ + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE = 0x1, + /* Fully connected bearers */ + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_CONNECTED = 0x2, + /* Multiplexed implies connected */ + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED = 0x4, +} MMIfaceModemCountBearersFlags; + +gboolean mm_iface_modem_count_bearers (MMIfaceModem *self, + MMIfaceModemCountBearersFlags flags, + guint *out_current, + guint *out_max, + GError **error); + #endif /* MM_IFACE_MODEM_H */ From 11489152d8e18d8fbcef4c7f1d46d901a19781f6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 6 Jul 2025 18:15:19 -0500 Subject: [PATCH 2/3] sleep-context: add ability to increase timeout Track when the timeout was started, so that if it needs to be increased we can calculate how long the new timeout should be. Then cancel the original timeout and start a new one with. Signed-off-by: Dan Williams --- src/mm-sleep-context.c | 33 ++++++++++++++++++++++++++++++--- src/mm-sleep-context.h | 3 +++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/mm-sleep-context.c b/src/mm-sleep-context.c index 69a9da22..d6c01198 100644 --- a/src/mm-sleep-context.c +++ b/src/mm-sleep-context.c @@ -27,6 +27,7 @@ G_DEFINE_TYPE_EXTENDED (MMSleepContext, mm_sleep_context, G_TYPE_OBJECT, 0, struct _MMSleepContextPrivate { GTask *task; guint timeout_id; + gint64 timeout_start; }; enum { @@ -52,6 +53,7 @@ timeout_cleanup (MMSleepContext *self) if (self->priv->timeout_id) g_source_remove (self->priv->timeout_id); self->priv->timeout_id = 0; + self->priv->timeout_start = 0; } void @@ -95,6 +97,33 @@ operation_ready (MMSleepContext *self, g_signal_emit (self, signals[DONE], 0, error); } +static void +timeout_add (MMSleepContext *self, + guint interval) +{ + timeout_cleanup (self); + self->priv->timeout_start = g_get_monotonic_time (); + self->priv->timeout_id = g_timeout_add_seconds (interval, + (GSourceFunc)operation_timeout, + self); +} + +void +mm_sleep_context_timeout_backoff (MMSleepContext *self, + guint more_seconds) +{ + gint64 cur_time; + gint64 new_interval; + + new_interval = more_seconds * G_USEC_PER_SEC; + if (self->priv->timeout_start) { + cur_time = g_get_monotonic_time (); + new_interval += (cur_time - self->priv->timeout_start); + } + + timeout_add (self, new_interval / G_USEC_PER_SEC); +} + /*****************************************************************************/ MMSleepContext * @@ -107,9 +136,7 @@ mm_sleep_context_new (guint timeout_seconds) NULL, (GAsyncReadyCallback)operation_ready, NULL); - self->priv->timeout_id = g_timeout_add_seconds (timeout_seconds, - (GSourceFunc)operation_timeout, - self); + timeout_add (self, timeout_seconds); mm_obj_dbg (self, "new context with %d second timeout", timeout_seconds); return self; diff --git a/src/mm-sleep-context.h b/src/mm-sleep-context.h index cd45c1f2..44b3c28a 100644 --- a/src/mm-sleep-context.h +++ b/src/mm-sleep-context.h @@ -51,6 +51,9 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSleepContext, g_object_unref) MMSleepContext *mm_sleep_context_new (guint timeout_seconds); +void mm_sleep_context_timeout_backoff (MMSleepContext *self, + guint more_seconds); + /* The MMSleepContext assumes ownership of @error */ void mm_sleep_context_complete (MMSleepContext *self, GError *error); From 01208cc6c0cd8c5121da68ca1e6468004d1d2418 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 7 Jul 2025 10:52:29 -0500 Subject: [PATCH 3/3] base-manager: increase suspend timeout for each active bearer Some modems can take a very long time (~20s) to reply to bearer disconnect requests especially in marginal signal situations where the modem must resend messages to the network after internal timeouts. Give the modem a chance to suspend successfully if there are active bearers. Related: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1311#note_2938638 Signed-off-by: Dan Williams --- src/mm-base-manager.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c index 0980b9f9..7ba2cdec 100644 --- a/src/mm-base-manager.c +++ b/src/mm-base-manager.c @@ -1167,6 +1167,7 @@ mm_base_manager_cleanup (MMBaseManager *self, GHashTableIter iter; MMDevice *device; gboolean disable, remove, low_power, terse; + guint active_bearers = 0; g_return_if_fail (self != NULL); g_return_if_fail (MM_IS_BASE_MANAGER (self)); @@ -1193,7 +1194,20 @@ mm_base_manager_cleanup (MMBaseManager *self, g_hash_table_iter_init (&iter, self->priv->devices); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) { DeviceCleanupContext *dctx; - GTask *task; + GTask *task; + MMBaseModem *modem; + + modem = mm_device_peek_modem (device); + if (modem) { + guint count = 0; + + mm_iface_modem_count_bearers (MM_IFACE_MODEM (modem), + MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE, + &count, + NULL, + NULL); + active_bearers += count; + } task = g_task_new (device, NULL, @@ -1201,12 +1215,18 @@ mm_base_manager_cleanup (MMBaseManager *self, ctx); ctx->tasks = g_slist_append (ctx->tasks, task); - dctx = device_cleanup_context_new (mm_device_peek_modem (device), flags); + dctx = device_cleanup_context_new (modem, flags); g_task_set_task_data (task, dctx, (GDestroyNotify)device_cleanup_context_free); device_cleanup_step (task); } + /* Increase cleanup timeout for each connected bearer; disconnecting a + * bearer can take many seconds. + */ + if (active_bearers) + mm_sleep_context_timeout_backoff (sleep_ctx, active_bearers * 20); + /* Complete cleanup if there were no devices */ manager_cleanup_context_maybe_complete (ctx, NULL, NULL, FALSE); }