power: introduce transition cancelling 51/287851/1
authorYoungjae Cho <y0.cho@samsung.com>
Fri, 20 Jan 2023 06:25:07 +0000 (15:25 +0900)
committerYoungjae Cho <y0.cho@samsung.com>
Tue, 7 Feb 2023 06:53:24 +0000 (15:53 +0900)
Cancelling transition reserves a new transition that undoing the ongoing
transition. That is, the power state will return to the previous state
after finishing the ongoing transition. Additionally with that
reservation, it discards all the pending transitions except one that is
to the poweroff/reboot/exit state.

Change-Id: Ib05841fa91162109db1c05b2f5e0a3e7edd3b90c
Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
src/power/power-dbus.c
src/power/power.c
src/power/power.h

index bf0e36f..6ffd260 100644 (file)
@@ -397,6 +397,29 @@ out:
        return g_variant_new("(i)", ret);
 }
 
+static GVariant *dbus_power_cancel_change_state_wait(GDBusConnection *conn,
+       const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
+       GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
+{
+       int ret = 0;
+       pid_t pid;
+       guint64 wait_callback_id;
+
+       pid = gdbus_connection_get_sender_pid(conn, sender);
+       if (pid == -1 || kill(pid, 0) == -1) {
+               _E("%d process does not exist, dbus ignored.", pid);
+               ret = -ESRCH;
+               goto out;
+       }
+
+       g_variant_get(param, "(t)", &wait_callback_id);
+
+       ret = cancel_change_state_wait(pid, wait_callback_id);
+
+out:
+       return g_variant_new("(i)", ret);
+}
+
 static GVariant *dbus_power_change_state(GDBusConnection *conn,
        const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
        GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
@@ -483,6 +506,7 @@ static const dbus_method_s dbus_methods[] = {
        { "AddChangeTransientStateWait",     "t",   "i",  dbus_power_add_change_transient_state_wait },
        { "RemoveChangeTransientStateWait",  "t",   NULL, dbus_power_remove_change_transient_state_wait },
        { "ConfirmChangeStateWait",          "t",   "i",  dbus_power_confirm_change_state_wait },
+       { "CancelChangeStateWait",           "t",   "i",  dbus_power_cancel_change_state_wait },
        { "PowerChangeState",                "t",   "i",  dbus_power_change_state },
        { "PowerChangeStateWithReason",      "ti",  NULL, dbus_power_change_state_with_reason },
        { "PowerGetState",                   NULL,  "t",  dbus_power_get_state },
index 03cff97..131a5e2 100644 (file)
@@ -39,6 +39,8 @@
 #define POWER_CONF_FILE              "/etc/deviced/power.conf"
 #define DEFAULT_MAX_WAIT_SECOND      5 /* second */
 #define MAX_DESC_LEN                 256
+#define TRANSITION_CONFIRM           0
+#define TRANSITION_CANCEL            1
 
 static int delayed_init_done = 0;
 static uint64_t current = DEVICED_POWER_STATE_START; /* current power state */
@@ -51,6 +53,12 @@ static struct {
        GList *waitings;
        int transient_step;
        int max_wait_timer;
+       /**
+        * If the cancel is set during a transition,
+        * undo the transition instead of doing action
+        * on reaching the destination state.
+        */
+       int cancel;
 } transition_context;
 
 struct proc_info {
@@ -213,7 +221,7 @@ static gint find_csw_by_pid_id(gconstpointer data, gconstpointer udata)
        return -1;
 }
 
-int confirm_change_state_wait(pid_t pid, guint64 transition_id)
+static int handle_change_state_wait(pid_t pid, guint64 transition_id, int transition_cancel)
 {
        struct proc_info target_pi = { .pid = pid };
        struct change_state_wait target_csw = { .pi = &target_pi, .transition_id = transition_id };
@@ -228,7 +236,10 @@ int confirm_change_state_wait(pid_t pid, guint64 transition_id)
                return 0;
 
        csw = l->data;
-       _D("pid=%d(%s) confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->transition_id, state_name(csw->state));
+       if (transition_cancel == TRANSITION_CANCEL)
+               CRITICAL_LOG("pid=%d(%s) cancelled id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->transition_id, state_name(csw->state));
+       else
+               _D("pid=%d(%s) confirmed id=%"PRIu64"(%s)", csw->pi->pid, csw->pi->comm, csw->transition_id, state_name(csw->state));
 
        transition_context.waitings = g_list_remove_link(transition_context.waitings, l);
        free(g_steal_pointer(&l->data));
@@ -247,6 +258,22 @@ int confirm_change_state_wait(pid_t pid, guint64 transition_id)
        return 0;
 }
 
+int confirm_change_state_wait(pid_t pid, guint64 transition_id)
+{
+       return handle_change_state_wait(pid, transition_id, TRANSITION_CONFIRM);
+}
+
+int cancel_change_state_wait(pid_t pid, guint64 transition_id)
+{
+       /* do not allow cancelling poweroff transition */
+       if (is_poweroff_state(transition_context.ti.next))
+               return handle_change_state_wait(pid, transition_id, TRANSITION_CONFIRM);
+
+       transition_context.cancel = 1;
+
+       return handle_change_state_wait(pid, transition_id, TRANSITION_CANCEL);
+}
+
 static uint64_t alloc_unique_id(void)
 {
        static uint64_t id = 0;
@@ -347,19 +374,12 @@ static int dequeue_transition(struct trans_info *ti)
 {
        struct trans_info *tail = NULL;
 
-       if (!ti)
-               return -1;
-
        tail = g_queue_pop_tail(transition_queue);
        if (!tail)
                return -1;
 
-       *ti = (struct trans_info) {
-               .curr = tail->curr,
-               .next = tail->next,
-               .reason = tail->reason,
-               .data = tail->data,
-       };
+       if (ti)
+               *ti = *tail;
 
        free(tail);
 
@@ -377,6 +397,8 @@ static void enqueue_transition(const struct trans_info *ti)
        if (!ti_new)
                return;
 
+       memcpy(ti_new, ti, sizeof(struct trans_info));
+
        g_queue_push_head(transition_queue, memcpy(ti_new, ti, sizeof(struct trans_info)));
 }
 
@@ -553,6 +575,55 @@ static void trigger_transition(void)
                action_transition();
 }
 
+static void cancel_transition(void)
+{
+       struct trans_info *ti = NULL;
+
+       struct trans_info poweroff = { .next = DEVICED_POWER_STATE_UNDEFINED };
+       struct trans_info undo = {
+               .curr = transition_context.ti.next,
+               .next = transition_context.ti.curr,
+               /**
+                * TODO: Is it needed that a reason for this cancelling transition, such as
+                *       DEVICE_POWER_TRANSITION_REASON_CANCEL_TRANSITION. Or use the previous
+                *       reason as it is?
+                */
+               .reason = transition_context.ti.reason,
+               .data = transition_context.ti.data,
+       };
+
+       assert(transition_context.ongoing);
+       assert(transition_context.cancel);
+
+       /**
+        * Discard all pending transitions except transition to the poweroff/reboot/exit state.
+        * Transition to the powerff/reboot/exit must remain intact in any case.
+        */
+       while ((ti = g_queue_peek_tail(transition_queue))) {
+               if (is_poweroff_state(ti->next)) {
+                       /**
+                        * Save the transition.
+                        *
+                        * Fix the poweroff.curr to next of the previous one. This is because the transition_queue always
+                        * manage its entries be chaining each other by matching those previous and next state in order.
+                        */
+                       dequeue_transition(&poweroff);
+                       poweroff.curr = undo.next;
+               } else {
+                       /* Discard the transition */
+                       CRITICAL_LOG("Discard pending transition, curr=%s, next=%s, reason=%d",
+                               state_abbr_name(ti->curr), state_abbr_name(ti->next), ti->reason);
+                       dequeue_transition(NULL);
+               }
+       }
+
+       /* Reserve transition for undoing. */
+       enqueue_transition(&undo);
+       /* Reserve transition for poweroff. */
+       if (is_poweroff_state(poweroff.next))
+               enqueue_transition(&poweroff);
+}
+
 static void action_transition(void)
 {
        uint64_t state = get_next_state();
@@ -566,9 +637,11 @@ static void action_transition(void)
                int index;
 
                current = transition_context.ti.next;
-               index = DEVICED_POWER_STATE_INDEX(current);
+               index = DEVICED_POWER_STATE_INDEX(transition_context.ti.next);
 
-               if (action_on_state[index])
+               if (transition_context.cancel)
+                       cancel_transition();
+               else if (action_on_state[index])
                        action_on_state[index](transition_context.ti.data);
 
                // transition end
@@ -604,6 +677,7 @@ static void prepare_transition(const struct trans_info *ti)
 
        transition_context.ongoing = 0;
        transition_context.transient_step = 0;
+       transition_context.cancel = 0;
 
        transition_description(1);
 }
index e303f66..8de8bb7 100644 (file)
@@ -152,5 +152,6 @@ uint64_t power_get_state(void);
 int add_change_state_wait(pid_t pid, guint64 state);
 void remove_change_state_wait(pid_t pid, guint64 state);
 int confirm_change_state_wait(pid_t pid, guint64 transition_id);
+int cancel_change_state_wait(pid_t pid, guint64 transition_id);
 
 #endif //__POWER_STATE_MANAGER_H__