diff options
author | Aleksander Morgado <aleksandermj@chromium.org> | 2024-07-11 17:00:57 +0000 |
---|---|---|
committer | Aleksander Morgado <aleksandermj@chromium.org> | 2024-09-30 07:54:56 +0000 |
commit | a24a6ee4c5af47859e433fe9559946515fb1869c (patch) | |
tree | 35cebc56093c113ef9994d7ad0ef201c171185f2 | |
parent | 8e8e88b75df329eb065e11491bbe5420c75cfd4e (diff) |
base-modem: add setup to allow exclusive operations
There are certain operations that cannot be run in parallel, e.g. one
should not disable and bring the modem to low power mode while there
are also incoming requests to enable the modem. This type of errors
will typically happen when multiple DBus clients are trying to use the
same modem at the same time for different purposes.
We do not have any way to have DBus clients take exclusive ownership
of a given modem object, so the best we can do for now is trying to
synchronize operations that cannot be run together, preserving the
order in which they arrived, and running them one by one.
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/mm-base-modem.c | 205 | ||||
-rw-r--r-- | src/mm-base-modem.h | 52 |
3 files changed, 250 insertions, 8 deletions
diff --git a/src/meson.build b/src/meson.build index 21ae576e..67b6edeb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -208,6 +208,7 @@ libport_dep = declare_dependency( # Daemon enums, required by plugins headers = files( 'mm-base-bearer.h', + 'mm-base-modem.h', 'mm-filter.h', 'mm-port-probe.h', ) diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c index d8fd4164..f1979689 100644 --- a/src/mm-base-modem.c +++ b/src/mm-base-modem.c @@ -36,6 +36,7 @@ #include "mm-log-object.h" #include "mm-port-enums-types.h" +#include "mm-daemon-enums-types.h" #include "mm-serial-parsers.h" #include "mm-modem-helpers.h" @@ -138,6 +139,9 @@ struct _MMBaseModemPrivate { /* Additional port links grabbed after having * organized ports */ GHashTable *link_ports; + + /* Scheduled operations */ + GList *scheduled_operations; }; guint @@ -1716,6 +1720,206 @@ mm_base_modem_authorize (MMBaseModem *self, } /*****************************************************************************/ +/* Exclusive operation */ + +typedef struct { + gssize id; + MMBaseModemOperationPriority priority; + gchar *description; + GTask *wait_task; +} OperationInfo; + +static void +operation_info_free (OperationInfo *info) +{ + g_assert (!info->wait_task); + g_free (info->description); + g_slice_free (OperationInfo, info); +} + +/* Exclusive operation lock */ + +gssize +mm_base_modem_operation_lock_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_int (G_TASK (res), error); +} + +static void +base_modem_operation_run (MMBaseModem *self) +{ + OperationInfo *info; + GTask *task; + + if (!self->priv->scheduled_operations) + return; + + /* Do nothing if head operation is already running */ + info = (OperationInfo *)(self->priv->scheduled_operations->data); + if (!info->wait_task) + return; + + /* Run the operation in head of the list */ + mm_obj_dbg (self, "[operation %" G_GSSIZE_FORMAT "] %s - %s: lock acquired", + info->id, + mm_base_modem_operation_priority_get_string (info->priority), + info->description); + task = g_steal_pointer (&info->wait_task); + g_task_return_int (task, info->id); + g_object_unref (task); +} + +void +mm_base_modem_operation_lock (MMBaseModem *self, + MMBaseModemOperationPriority priority, + const gchar *description, + GAsyncReadyCallback callback, + gpointer user_data) +{ + OperationInfo *info; + static gssize operation_id = 0; + + info = g_slice_new0 (OperationInfo); + info->id = operation_id; + info->priority = priority; + info->description = g_strdup (description); + info->wait_task = g_task_new (self, NULL, callback, user_data); + + if (operation_id == G_MAXSSIZE) { + mm_obj_dbg (self, "operation id reset"); + operation_id = 0; + } else + operation_id++; + + mm_obj_dbg (self, "[operation %" G_GSSIZE_FORMAT "] %s - %s: scheduled", + info->id, + mm_base_modem_operation_priority_get_string (info->priority), + info->description); + self->priv->scheduled_operations = g_list_append (self->priv->scheduled_operations, info); + + base_modem_operation_run (self); +} + +/* Exclusive operation unlock */ + +void +mm_base_modem_operation_unlock (MMBaseModem *self, + gssize operation_id) +{ + OperationInfo *info; + + g_assert (self->priv->scheduled_operations); + + info = (OperationInfo *)(self->priv->scheduled_operations->data); + g_assert (!info->wait_task); + g_assert (info->id == operation_id); + + mm_obj_dbg (self, "[operation %" G_GSSIZE_FORMAT "] %s - %s: lock released", + info->id, + mm_base_modem_operation_priority_get_string (info->priority), + info->description); + + /* Remove head list item and free its contents */ + self->priv->scheduled_operations = g_list_delete_link (self->priv->scheduled_operations, + self->priv->scheduled_operations); + operation_info_free (info); + + /* Run next, if any */ + base_modem_operation_run (self); +} + +/*****************************************************************************/ + +typedef struct { + GDBusMethodInvocation *invocation; + MMBaseModemOperationPriority operation_priority; + gchar *operation_description; +} AuthorizeAndOperationLockContext; + +static void +authorize_and_operation_lock_context_free (AuthorizeAndOperationLockContext *ctx) +{ + g_object_unref (ctx->invocation); + g_free (ctx->operation_description); + g_slice_free (AuthorizeAndOperationLockContext, ctx); +} + +gssize +mm_base_modem_authorize_and_operation_lock_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_int (G_TASK (res), error); +} + +static void +lock_after_authorize_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + gssize operation_id; + + operation_id = mm_base_modem_operation_lock_finish (self, res, &error); + if (operation_id < 0) + g_task_return_error (task, error); + else + g_task_return_int (task, operation_id); + g_object_unref (task); +} + +static void +authorize_before_lock_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + AuthorizeAndOperationLockContext *ctx; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + mm_base_modem_operation_lock (self, + ctx->operation_priority, + ctx->operation_description, + (GAsyncReadyCallback) lock_after_authorize_ready, + task); +} + +void +mm_base_modem_authorize_and_operation_lock (MMBaseModem *self, + GDBusMethodInvocation *invocation, + const gchar *authorization, + MMBaseModemOperationPriority operation_priority, + const gchar *operation_description, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + AuthorizeAndOperationLockContext *ctx; + + task = g_task_new (self, NULL, callback, user_data); + + ctx = g_slice_new0 (AuthorizeAndOperationLockContext); + ctx->invocation = g_object_ref (invocation); + ctx->operation_priority = operation_priority; + ctx->operation_description = g_strdup (operation_description); + g_task_set_task_data (task, ctx, (GDestroyNotify)authorize_and_operation_lock_context_free); + + mm_base_modem_authorize (self, + invocation, + authorization, + (GAsyncReadyCallback)authorize_before_lock_ready, + task); +} + +/*****************************************************************************/ const gchar * mm_base_modem_get_device (MMBaseModem *self) @@ -2020,6 +2224,7 @@ finalize (GObject *object) g_assert (!self->priv->enable_tasks); g_assert (!self->priv->disable_tasks); + g_assert (!self->priv->scheduled_operations); mm_obj_dbg (self, "completely disposed"); diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h index 39e203f9..24dfcdc9 100644 --- a/src/mm-base-modem.h +++ b/src/mm-base-modem.h @@ -227,14 +227,50 @@ guint mm_base_modem_get_subsystem_vendor_id (MMBaseModem *self); GCancellable *mm_base_modem_peek_cancellable (MMBaseModem *self); -void mm_base_modem_authorize (MMBaseModem *self, - GDBusMethodInvocation *invocation, - const gchar *authorization, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean mm_base_modem_authorize_finish (MMBaseModem *self, - GAsyncResult *res, - GError **error); +/******************************************************************************/ +/* Polkit */ + +void mm_base_modem_authorize (MMBaseModem *self, + GDBusMethodInvocation *invocation, + const gchar *authorization, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_base_modem_authorize_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error); + +/******************************************************************************/ +/* Operation lock support */ + +typedef enum { /*< underscore_name=mm_base_modem_operation_priority >*/ + /* Default operations are scheduled at the end of the list of pending + * operations */ + MM_BASE_MODEM_OPERATION_PRIORITY_DEFAULT, +} MMBaseModemOperationPriority; + +void mm_base_modem_operation_lock (MMBaseModem *self, + MMBaseModemOperationPriority priority, + const gchar *description, + GAsyncReadyCallback callback, + gpointer user_data); +gssize mm_base_modem_operation_lock_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error); +void mm_base_modem_operation_unlock (MMBaseModem *self, + gssize operation_id); + +void mm_base_modem_authorize_and_operation_lock (MMBaseModem *self, + GDBusMethodInvocation *invocation, + const gchar *authorization, + MMBaseModemOperationPriority operation_priority, + const gchar *operation_description, + GAsyncReadyCallback callback, + gpointer user_data); +gssize mm_base_modem_authorize_and_operation_lock_finish (MMBaseModem *self, + GAsyncResult *res, + GError **error); + +/******************************************************************************/ void mm_base_modem_initialize (MMBaseModem *self, GAsyncReadyCallback callback, |