power: fix suspend_sys_sync_wait()
Since the workqueue code deletes the work before executing it,
checking for no work item being currently queued to the workqueue
is not sufficient to guarantee that all the works have finished
execution.
Use a counter to guarantee that all the pending suspend_sys_sync()
works have finished execution before returning from
suspend_sys_sync_wait().
CRs-Fixed: 293595
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
Conflicts:
kernel/power/wakelock.c
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index 2ee459f..892e3ec 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -44,6 +44,10 @@
static LIST_HEAD(inactive_locks);
static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];
static int current_event_num;
+static int suspend_sys_sync_count;
+static DEFINE_SPINLOCK(suspend_sys_sync_lock);
+static struct workqueue_struct *suspend_sys_sync_work_queue;
+static DECLARE_COMPLETION(suspend_sys_sync_comp);
struct workqueue_struct *suspend_work_queue;
struct wake_lock main_wake_lock;
suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;
@@ -255,6 +259,70 @@
return ret;
}
+static void suspend_sys_sync(struct work_struct *work)
+{
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("PM: Syncing filesystems...\n");
+
+ sys_sync();
+
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("sync done.\n");
+
+ spin_lock(&suspend_sys_sync_lock);
+ suspend_sys_sync_count--;
+ spin_unlock(&suspend_sys_sync_lock);
+}
+static DECLARE_WORK(suspend_sys_sync_work, suspend_sys_sync);
+
+void suspend_sys_sync_queue(void)
+{
+ int ret;
+
+ spin_lock(&suspend_sys_sync_lock);
+ ret = queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work);
+ if (ret)
+ suspend_sys_sync_count++;
+ spin_unlock(&suspend_sys_sync_lock);
+}
+
+static bool suspend_sys_sync_abort;
+static void suspend_sys_sync_handler(unsigned long);
+static DEFINE_TIMER(suspend_sys_sync_timer, suspend_sys_sync_handler, 0, 0);
+/* value should be less then half of input event wake lock timeout value
+ * which is currently set to 5*HZ (see drivers/input/evdev.c)
+ */
+#define SUSPEND_SYS_SYNC_TIMEOUT (HZ/4)
+static void suspend_sys_sync_handler(unsigned long arg)
+{
+ if (suspend_sys_sync_count == 0) {
+ complete(&suspend_sys_sync_comp);
+ } else if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
+ suspend_sys_sync_abort = true;
+ complete(&suspend_sys_sync_comp);
+ } else {
+ mod_timer(&suspend_sys_sync_timer, jiffies +
+ SUSPEND_SYS_SYNC_TIMEOUT);
+ }
+}
+
+int suspend_sys_sync_wait(void)
+{
+ suspend_sys_sync_abort = false;
+
+ if (suspend_sys_sync_count != 0) {
+ mod_timer(&suspend_sys_sync_timer, jiffies +
+ SUSPEND_SYS_SYNC_TIMEOUT);
+ wait_for_completion(&suspend_sys_sync_comp);
+ }
+ if (suspend_sys_sync_abort) {
+ pr_info("suspend aborted....while waiting for sys_sync\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
static void suspend(struct work_struct *work)
{
int ret;