aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksandermj@chromium.org>2024-07-11 17:00:57 +0000
committerAleksander Morgado <aleksandermj@chromium.org>2024-09-30 07:54:56 +0000
commita24a6ee4c5af47859e433fe9559946515fb1869c (patch)
tree35cebc56093c113ef9994d7ad0ef201c171185f2
parent8e8e88b75df329eb065e11491bbe5420c75cfd4e (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.build1
-rw-r--r--src/mm-base-modem.c205
-rw-r--r--src/mm-base-modem.h52
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,