From d5f3a6d441f47836698b851d6049bf738d066ef6 Mon Sep 17 00:00:00 2001 From: Dylan Van Assche Date: Sat, 1 May 2021 15:50:47 +0200 Subject: [PATCH 01/39] base-manager: add quick suspend/resume base Quick suspend/resume infrastructure for synchronizing the interfaces when resuming. --- src/main.c | 14 +++- src/mm-base-manager.c | 48 ++++++++++++ src/mm-base-manager.h | 4 + src/mm-base-modem.c | 155 +++++++++++++++++++++++++++++++++++++++ src/mm-base-modem.h | 23 ++++++ src/mm-broadband-modem.c | 81 ++++++++++++++++++++ src/mm-context.c | 11 +++ src/mm-context.h | 1 + 8 files changed, 336 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 928078a3..4a036971 100644 --- a/src/main.c +++ b/src/main.c @@ -72,6 +72,13 @@ resuming_cb (MMSleepMonitor *sleep_monitor) mm_base_manager_start (manager, FALSE); } +static void +resuming_quick_cb (MMSleepMonitor *sleep_monitor) +{ + mm_dbg ("syncing modem state (quick resuming)"); + mm_base_manager_sync (manager); +} + #endif static void @@ -197,7 +204,12 @@ main (int argc, char *argv[]) if (mm_context_get_test_no_suspend_resume()) mm_dbg ("Suspend/resume support disabled at runtime"); - else { + else if (mm_context_get_test_quick_suspend_resume()) { + mm_dbg ("Quick suspend/resume hooks enabled"); + sleep_monitor = mm_sleep_monitor_get (); + g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_quick_cb), NULL); + } else { + mm_dbg ("Full suspend/resume hooks enabled"); sleep_monitor = mm_sleep_monitor_get (); g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_SLEEPING, G_CALLBACK (sleeping_cb), NULL); g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_cb), NULL); diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c index 130e5ad1..0c393f11 100644 --- a/src/mm-base-manager.c +++ b/src/mm-base-manager.c @@ -45,6 +45,7 @@ #include "mm-plugin.h" #include "mm-filter.h" #include "mm-log-object.h" +#include "mm-base-modem.h" static void initable_iface_init (GInitableIface *iface); static void log_object_iface_init (MMLogObjectInterface *iface); @@ -663,6 +664,53 @@ mm_base_manager_num_modems (MMBaseManager *self) return n; } +/*****************************************************************************/ +/* Quick resume synchronization */ + +#if defined WITH_SYSTEMD_SUSPEND_RESUME + +gboolean mm_base_modem_sync_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +mm_base_modem_sync_ready (MMBaseModem *self, + GAsyncResult *res, + gpointer user_data) +{ + g_autoptr(GError) error; + + mm_base_modem_sync_finish (self, res, &error); + if (error) { + mm_obj_warn (self, "synchronization failed"); + return; + } + mm_obj_info (self, "synchronization finished"); +} + +void +mm_base_manager_sync (MMBaseManager *self) +{ + GHashTableIter iter; + gpointer key, value; + + g_return_if_fail (self != NULL); + g_return_if_fail (MM_IS_BASE_MANAGER (self)); + + /* Refresh each device */ + g_hash_table_iter_init (&iter, self->priv->devices); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MMBaseModem *modem = mm_device_peek_modem (MM_DEVICE (value)); + /* We just want to start the synchronization, we don't need the result */ + mm_base_modem_sync (modem, (GAsyncReadyCallback)mm_base_modem_sync_ready, NULL); + } +} + +#endif + /*****************************************************************************/ /* Set logging */ diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h index d70fa08f..be51d0c0 100644 --- a/src/mm-base-manager.h +++ b/src/mm-base-manager.h @@ -66,6 +66,10 @@ void mm_base_manager_start (MMBaseManager *manager, void mm_base_manager_shutdown (MMBaseManager *manager, gboolean disable); +#if defined WITH_SYSTEMD_SUSPEND_RESUME +void mm_base_manager_sync (MMBaseManager *manager); +#endif + guint32 mm_base_manager_num_modems (MMBaseManager *manager); #endif /* MM_BASE_MANAGER_H */ diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c index 926d0712..38f43f8c 100644 --- a/src/mm-base-modem.c +++ b/src/mm-base-modem.c @@ -369,6 +369,161 @@ mm_base_modem_grab_port (MMBaseModem *self, return TRUE; } +/******************************************************************************/ + +typedef struct { + gchar *name; + gulong link_port_grabbed_id; + guint timeout_id; +} WaitLinkPortContext; + +static void +wait_link_port_context_free (WaitLinkPortContext *ctx) +{ + g_assert (!ctx->link_port_grabbed_id); + g_assert (!ctx->timeout_id); + g_free (ctx->name); + g_slice_free (WaitLinkPortContext, ctx); +} + +MMPort * +mm_base_modem_wait_link_port_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static gboolean +wait_link_port_timeout_cb (GTask *task) +{ + WaitLinkPortContext *ctx; + MMBaseModem *self; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + ctx->timeout_id = 0; + g_signal_handler_disconnect (self, ctx->link_port_grabbed_id); + ctx->link_port_grabbed_id = 0; + + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "Timed out waiting for link port 'net/%s'", + ctx->name); + g_object_unref (task); + + return G_SOURCE_REMOVE; +} + +static void +wait_link_port_grabbed_cb (MMBaseModem *self, + MMPort *link_port, + GTask *task) +{ + WaitLinkPortContext *ctx; + MMPortSubsys link_port_subsystem; + const gchar *link_port_name; + + ctx = g_task_get_task_data (task); + + link_port_subsystem = mm_port_get_subsys (link_port); + link_port_name = mm_port_get_device (link_port); + + if (link_port_subsystem != MM_PORT_SUBSYS_NET) { + mm_obj_warn (self, "unexpected link port subsystem grabbed: %s/%s", + mm_port_subsys_get_string (link_port_subsystem), + link_port_name); + return; + } + + if (g_strcmp0 (link_port_name, ctx->name) != 0) + return; + + /* we got it! */ + + g_source_remove (ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect (self, ctx->link_port_grabbed_id); + ctx->link_port_grabbed_id = 0; + + g_task_return_pointer (task, g_object_ref (link_port), g_object_unref); + g_object_unref (task); +} + +void +mm_base_modem_wait_link_port (MMBaseModem *self, + const gchar *subsystem, + const gchar *name, + guint timeout_ms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + WaitLinkPortContext *ctx; + GTask *task; + g_autofree gchar *key = NULL; + MMPort *port; + + task = g_task_new (self, NULL, callback, user_data); + + if (!g_str_equal (subsystem, "net")) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Cannot wait for port '%s/%s', unexpected link port subsystem", subsystem, name); + g_object_unref (task); + return; + } + + key = g_strdup_printf ("%s%s", subsystem, name); + port = g_hash_table_lookup (self->priv->link_ports, key); + if (port) { + mm_obj_dbg (self, "no need to wait for port '%s/%s': already grabbed", subsystem, name); + g_task_return_pointer (task, g_object_ref (port), g_object_unref); + g_object_unref (task); + return; + } + + ctx = g_slice_new0 (WaitLinkPortContext); + ctx->name = g_strdup (name); + g_task_set_task_data (task, ctx, (GDestroyNotify)wait_link_port_context_free); + + /* task ownership shared between timeout and signal handler */ + ctx->timeout_id = g_timeout_add (timeout_ms, + (GSourceFunc) wait_link_port_timeout_cb, + task); + ctx->link_port_grabbed_id = g_signal_connect (self, + MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED, + G_CALLBACK (wait_link_port_grabbed_cb), + task); + + mm_obj_dbg (self, "waiting for port '%s/%s'...", subsystem, name); +} + +/******************************************************************************/ + +static void +mm_base_modem_sync_ready (MMBaseModem *self, + GAsyncResult *res) +{ + g_autoptr (GError) error = NULL; + + MM_BASE_MODEM_GET_CLASS (self)->sync_finish (self, res, &error); + if (error) { + mm_obj_warn (self, "synchronization failed"); + } +} + +void +mm_base_modem_sync (MMBaseModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_assert (MM_BASE_MODEM_GET_CLASS (self)->sync != NULL); + g_assert (MM_BASE_MODEM_GET_CLASS (self)->sync_finish != NULL); + + MM_BASE_MODEM_GET_CLASS (self)->sync (self, + (GAsyncReadyCallback) mm_base_modem_sync_ready, + NULL); +} + gboolean mm_base_modem_disable_finish (MMBaseModem *self, GAsyncResult *res, diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h index 24634814..75fd99f5 100644 --- a/src/mm-base-modem.h +++ b/src/mm-base-modem.h @@ -101,6 +101,22 @@ struct _MMBaseModemClass { gboolean (*disable_finish) (MMBaseModem *self, GAsyncResult *res, GError **error); + + /* Modem synchronization. + * When resuming in quick suspend/resume mode, + * this method triggers a synchronization of all modem interfaces */ + void (* sync) (MMBaseModem *self, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* sync_finish) (MMBaseModem *self, + GAsyncResult *res, + GError **error); + + /* signals */ + void (* link_port_grabbed) (MMBaseModem *self, + MMPort *link_port); + void (* link_port_released) (MMBaseModem *self, + MMPort *link_port); }; GType mm_base_modem_get_type (void); @@ -199,6 +215,13 @@ gboolean mm_base_modem_disable_finish (MMBaseModem *self, GAsyncResult *res, GError **error); +void mm_base_modem_sync (MMBaseModem *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_base_modem_sync_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error); + void mm_base_modem_process_sim_event (MMBaseModem *self); #endif /* MM_BASE_MODEM_H */ diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index a25883ac..6e47757e 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -11337,6 +11337,82 @@ enable (MMBaseModem *self, g_object_unref (task); } +/*****************************************************************************/ + +#if defined WITH_SYSTEMD_SUSPEND_RESUME + +typedef enum { + SYNCING_STEP_FIRST, + SYNCING_STEP_LAST, +} SyncingStep; + +typedef struct { + SyncingStep step; +} SyncingContext; + +static gboolean +synchronize_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +syncing_step (GTask *task) +{ + MMBroadbandModem *self; + SyncingContext *ctx; + + /* Don't run new steps if we're cancelled */ + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + switch (ctx->step) { + case SYNCING_STEP_FIRST: + ctx->step++; + /* fall through */ + + case SYNCING_STEP_LAST: + mm_obj_info (self, "resume synchronization state (%d/%d): all done", + ctx->step, SYNCING_STEP_LAST); + /* We are done without errors! */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + + default: + break; + } + + g_assert_not_reached (); +} + +/* 'sync' as function name conflicts with a declared function in unistd.h */ +static void +synchronize (MMBaseModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SyncingContext *ctx; + GTask *task; + + /* Create SyncingContext */ + ctx = g_new0 (SyncingContext, 1); + ctx->step = SYNCING_STEP_FIRST; + + /* Create sync steps task and execute it */ + task = g_task_new (MM_BROADBAND_MODEM (self), NULL, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); + syncing_step (task); +} + +#endif /*****************************************************************************/ @@ -12683,6 +12759,11 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass) base_modem_class->disable = disable; base_modem_class->disable_finish = disable_finish; +#if defined WITH_SYSTEMD_SUSPEND_RESUME + base_modem_class->sync = synchronize; + base_modem_class->sync_finish = synchronize_finish; +#endif + klass->setup_ports = setup_ports; klass->initialization_started = initialization_started; klass->initialization_started_finish = initialization_started_finish; diff --git a/src/mm-context.c b/src/mm-context.c index 6561127e..691ca82b 100644 --- a/src/mm-context.c +++ b/src/mm-context.c @@ -227,6 +227,7 @@ static gboolean test_no_udev; #endif #if defined WITH_SYSTEMD_SUSPEND_RESUME static gboolean test_no_suspend_resume; +static gboolean test_quick_suspend_resume; #endif static const GOptionEntry test_entries[] = { @@ -258,6 +259,11 @@ static const GOptionEntry test_entries[] = { "Disable suspend/resume support at runtime even if available", NULL }, + { + "test-quick-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_quick_suspend_resume, + "Enable quick suspend/resume support for modems which stay on during host suspension", + NULL + }, #endif { NULL } }; @@ -308,6 +314,11 @@ mm_context_get_test_no_suspend_resume (void) { return test_no_suspend_resume; } +gboolean +mm_context_get_test_quick_suspend_resume (void) +{ + return test_quick_suspend_resume; +} #endif /*****************************************************************************/ diff --git a/src/mm-context.h b/src/mm-context.h index 721fee88..276567fd 100644 --- a/src/mm-context.h +++ b/src/mm-context.h @@ -51,6 +51,7 @@ gboolean mm_context_get_test_no_udev (void); #endif #if defined WITH_SYSTEMD_SUSPEND_RESUME gboolean mm_context_get_test_no_suspend_resume (void); +gboolean mm_context_get_test_quick_suspend_resume (void); #endif #endif /* MM_CONTEXT_H */ -- 2.31.1